What Context Does the Scheduler Code Run In

What context does the scheduler code run in?

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.

Linux kernel: What process does schedule() run in?

schedule() is always running in process context. The special part about it is that it can change which process context is current - but it does always have a process context. Prior to the call to context_switch() it runs in the context of the process to be swapped out, and after it runs in the process swapped in.

The Linux kernel does not have a dedicated "swapper" task (there is an idle task, which is always runnable in case nothing else is eligible to run).

In what context Kernel Thread runs in Linux?

Kernel threads run in process contexts in kernel space. Though there are some kernel threads which handle interrupts too. They are called threaded interrupt handlers. But they still run in process context!

Here is an excellent explanation of the vital difference between the 2 contexts!

Here is one which'll help you gain more understanding!

What context does the scheduler code run in?

As far as I know, process and interrupt are the only 2 contexts.

Can I add context in @Scheduled?

You can try to implement custom TaskScheduler and register it in SchedulingConfigurer.configureTasks. ConcurrentTaskScheduler can be considered as an example. Unfortunately this class isn't well-designed for inheritance, otherwise decorateTask method will be protected. So you need to override all methods to add one additional Runnable decorator with your logic. Something like this one :

@Configuration
@EnableScheduling
public class ScheduledConfig implements SchedulingConfigurer {

public static class MyTaskScheduler extends ConcurrentTaskScheduler {

public MyTaskScheduler() {
}

public MyTaskScheduler(ScheduledExecutorService scheduledExecutor) {
super(scheduledExecutor);
}

public MyTaskScheduler(Executor concurrentExecutor, ScheduledExecutorService scheduledExecutor) {
super(concurrentExecutor, scheduledExecutor);
}

// TODO override other methods

@Override
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
return super.schedule(decorateTask(task), trigger);
}

private Runnable decorateTask(Runnable task) {
// not 100% sure about safety of this cast
return new MyRunnable((ScheduledMethodRunnable) task);
}

private static class MyRunnable implements Runnable {

private final ScheduledMethodRunnable runnable;
private final AtomicLong counter = new AtomicLong();

public MyRunnable(ScheduledMethodRunnable runnable) {
this.runnable = runnable;
}

@Override
public void run() {
System.out.println(runnable.getMethod().toGenericString() + " " + counter.incrementAndGet());
runnable.run();
}
}
}

@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
TaskScheduler taskScheduler = new MyTaskScheduler(Executors.newScheduledThreadPool(10));
taskRegistrar.setTaskScheduler(taskScheduler);
}

@Scheduled(cron = "0/1 * * * * *")
public void test() {
System.out.println("Running task in thread " + Thread.currentThread().getId());
}
}

Running scheduler in Spring boot is spawning a process external to Spring boot application context

The @Scheduled should work. Have you added the @EnabledScheduling annotation to a @Configuration or the @SpringBootApplication? The Scheduling Getting Started explains it in detail.

Regarding the scheduleTask method: What calls that? Is it started outside the Spring context? If yes then Spring won't stop it. You have to take care of the lifecycle.

You should try to use the @Scheduled as it will manage the thread pools/executors for you and most people will find it easier to understand.

Does the linux scheduler needs to be context switched?

That's a very good question, and the answer to it would be "yes" except for the fact that the hardware is aware of the concept of an OS and task scheduler.

In the hardware, you'll find registers that are restricted to "supervisor" mode. Without going into too much detail about the internal CPU architecture, there's a copy of the basic program execution registers for "user mode" and "supervisor mode," the latter of which can only be accessed by the OS itself (via a flag in a control register that the kernel sets which says whether or not the kernel or a user mode application is currently running).

So the "context switch" you speak of is the process of swapping/resetting the user mode registers (instruction register, stack pointer register, etc.) etc. but the system registers don't need to be swapped out because they're stored apart from the user ones.

For instance, the user mode stack in x86 is USP - A7, whereas the supervisor mode stack is SSP - A7. So the kernel itself (which contains the task scheduler) would use the supervisor mode stack and other supervisor mode registers to run itself, setting the supervisor mode flag to 1 when it's running, then perform a context switch on the user mode hardware to swap between apps and setting the supervisor mode flag to 0.

But prior to the idea of OSes and task scheduling, if you wanted to do a multitasking system then you'd have had to use the basic concept that you outlined in your question: use a hardware interrupt to call the task scheduler every x cycles, then swap out the app for the task scheduler, then swap in the new app. But in most cases the timer interrupt would be your actual task scheduler itself and it would have been heavily optimized to make it less of a context switch and more of a simple interrupt handler routine.

Context switch internals

At a high level, there are two separate mechanisms to understand. The first is the kernel entry/exit mechanism: this switches a single running thread from running usermode code to running kernel code in the context of that thread, and back again. The second is the context switch mechanism itself, which switches in kernel mode from running in the context of one thread to another.

So, when Thread A calls sched_yield() and is replaced by Thread B, what happens is:

  1. Thread A enters the kernel, changing from user mode to kernel mode;
  2. Thread A in the kernel context-switches to Thread B in the kernel;
  3. Thread B exits the kernel, changing from kernel mode back to user mode.

Each user thread has both a user-mode stack and a kernel-mode stack. When a thread enters the kernel, the current value of the user-mode stack (SS:ESP) and instruction pointer (CS:EIP) are saved to the thread's kernel-mode stack, and the CPU switches to the kernel-mode stack - with the int $80 syscall mechanism, this is done by the CPU itself. The remaining register values and flags are then also saved to the kernel stack.

When a thread returns from the kernel to user-mode, the register values and flags are popped from the kernel-mode stack, then the user-mode stack and instruction pointer values are restored from the saved values on the kernel-mode stack.

When a thread context-switches, it calls into the scheduler (the scheduler does not run as a separate thread - it always runs in the context of the current thread). The scheduler code selects a process to run next, and calls the switch_to() function. This function essentially just switches the kernel stacks - it saves the current value of the stack pointer into the TCB for the current thread (called struct task_struct in Linux), and loads a previously-saved stack pointer from the TCB for the next thread. At this point it also saves and restores some other thread state that isn't usually used by the kernel - things like floating point/SSE registers. If the threads being switched don't share the same virtual memory space (ie. they're in different processes), the page tables are also switched.

So you can see that the core user-mode state of a thread isn't saved and restored at context-switch time - it's saved and restored to the thread's kernel stack when you enter and leave the kernel. The context-switch code doesn't have to worry about clobbering the user-mode register values - those are already safely saved away in the kernel stack by that point.

Working of scheduler

The scheduler is a program, yes, but very rarely is it a process. Rather the schedule is part of the Kernel, or the program that abstracts processes from the hardware (including the processor usage).

In a preemptive scheduler, Since the scheduler is part of the kernel, it actually exists in the address space of every single process. When a process's alloted time is up, the scheduler takes control of program execution and then does the necessary work to move to the next process. When the schedule does this, however, it does not remove itself from the new process's address space so that when the new process's time is up, it can saftely perform the work needed to move on.

While there have been kernels whose functions were often offloaded into other processes (CMU Mach), there will always be a part of the kernel that retains functionality for changing processes, and this will never be exclusively in its own process.

For more information on how scheduling works, I find the following articles helpful:

http://wiki.osdev.org/Context_Switching

http://wiki.osdev.org/Scheduling_Algorithms

http://wiki.osdev.org/Processes_and_Threads



Related Topics



Leave a reply



Submit