There are two cases where the scheduler code schedule()
is invoked-
When a process voluntarily calls schedule()
Timer
__schedule() is the main scheduler function.
The main means of driving the scheduler and thus entering this function are:
Explicit blocking: mutex, semaphore, waitqueue, etc.
TIF_NEED_RESCHED flag is checked on interrupt and userspace return paths. For example, see arch/x86/entry_64.S. To drive preemption between tasks, the scheduler sets the flag in timer interrupt handler scheduler_tick().
Wakeups don't really cause entry into schedule(). They add a task to the run-queue and that's it. Now, if the new task added to the run-queue preempts the current task, then the wakeup sets TIF_NEED_RESCHED and schedule() gets called on the nearest possible occasion:
http://lxr.free-electrons.com/source/kernel/sched/core.c#L2389
schedule()
always runs in process context. In the second case, when it is initiated by a timer interrupt, it is in the return path back from the kernel to the interrupted process where schedule()
is called.
When a process calls schedule()
it runs in a system call context which is interrupt based. In the 2nd case a hardware interrupt triggers the schedule()
call. In both cases it runs as an interrupt. AFAIK those are the only times that schedule()
is called because most manipulation of scheduling involves modifying the kernel run queue of things to be scheduled although a process can be interrupted but that is usually done via an interrupt to tell the process to yield or the process yielding itself.