What you are describing is... uh, threads. this is exactly how most userland cooperative thread systems work.
Switching from one thread to another does require a switch in context, regardless - that's what threads are, the contexts of sequences of execution.
Note that a process abstraction represents a context for managing the multiplexing of resources, and consists of a set of resources allocated to the process (e.g., memory) to operate on. Between threads, sharing resources has to be moderated by the OS; within a process, all resources are at the disposal of everything else in the process, and the process has the responsibility of managing their use. If a process only uses a fixed amount of resources and never communicates with other processes (rare, but not impossible), then the state of the process is the states of its threads and its resources, nothing else.
A thread, on the other hand, is an abstraction representing the context of the execution of a set of operations (things like the condition of the registers and the stack at a given point in time). Threads exist within the context of a process. Every running process has at least one thread, even if it isn't formally defined as such and there is no existing provision for multiple local threads.
Switching between processes necessarily involves suspending all of the process's threads, but suspending a thread doesn't mean suspending the rest of the threads of a process (unless you use something like the Global Interpreter Lock in Python - something which is generally considered a Bad Idea).
Now, there are a number of ways that the context of a thread - or of a process as a whole - can be suspended. The first, which is pretty much necessary in any any multitasking system, is for the thread to cede control voluntarily, whether to permit other threads to operate, or to wait for some time period (sleep()), to wait for some operation to complete (wait()), or wait for a message from another thread/process (through whatever the IPC primitives being used are). In a cooperative multitasking system, the OS requires threads to voluntarily cede the processor frequently, and some systems (e.g., Oberon) require that compilers and interpreters insert sleep()s into the code automatically to ensure this.
The other major way is when processing an interrupt. Most multitasking systems will reschedule whenever an interrupt occurs, and preemptive multitasking systems use a timer (either with a regular 'tick' or by setting a new timer interrupt cycle each time a scheduling event occurs) to force reschedules periodically. Most OS kernels schedule process this way, but then leave the scheduling of threads to the processes themselves, though most OSes today have a standard threading library which the kernel scheduler is aware of and can work together with.
Some operating systems also provide 'kernel-scheduled threads', which are scheduled independently of each other by the kernel scheduler, and can even run on different CPUs in a multiprocessor platform, while still remaining within the same process context. They are generally more 'heavyweight' than process-local threads, but less so than separate processes.
However, most thread libraries schedule threads locally within a process - meaning that they provide concurrency (more than one thread running multiplexed over a period of time) but not true parallelism (more than one thread running simultaneously), which would require kernel intervention. There is some gray area on this, as it is possible for a process to be assigned multiple CPUs (or multiple independent time slices on different CPUs, depending on how the OS abstracts it) as 'resources' for its exclusive use, which it could then schedule locally, but AFAICT most stock OSes don't support that per se - it is usually only found on systems which are written (or at least tuned) for massively parallel hardware, especially ones designed for massive simultaneous vector processing operations.
Many threading libraries are based on cooperative scheduling, and count on the fact that most threads are I/O bound to allow them to balance the thread operations - compute-bound threads would be expected to cede frequently. Most libraries do have provisions for preemption, though, and the easiest way to provide that in the absence of kernel threading is for the scheduler itself to request that the kernel send a message after a given interval and have the scheduler sleep until either the current thread cedes or the timer message comes in. There are a lot of variations on this, but that's more or less how it is done as a rule.
Does this help, and if not, can you explain your ideas in depth a little more?
_________________ Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF Ordo OS Project Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
|