Linux 2.6 Kernel:
With the 2.6 kernel, a large step was taken to improve Linux's real-time capabilities. The improvements are a result of two major changes in the kernel (as well as many minor changes):
1. Kernel preemption. Before 2.6 the scheduler was able to preempt threads running in user mode, but when the thread made a system call that caused a context switch to kernel mode there was no way for the thread to be preempted. This situation could cause a high-priority thread that was ready to run to be blocked by a lower priority thread inside a system call. With 2.6, the kernel can now be preempted.
2. Constant time scheduler. Prior to version 2.6, the time that it took for the scheduler to decide which thread to run depended on how many threads that were currently running on the system. The more threads on the system, the more time it took the scheduler to make a decision. In version 2.6, the scheduler makes the decision in the same amount of time whether there are 1 thread or 100 threads on the system. This makes the system more predictable.
Scheduling Priorities:
Most processes are started with the default scheduler: SCHED_NORMAL. There are two other schedulers that can be used for real time threads:
1. SCHED_FIFO. This is a first-in first-out scheduler. The highest priority SCHED_FIFO thread ready to run will be scheduled. Once the thread starts running it will only be preempted if a higher priority real time thread becomes runnable or if the thread yields the processor.
2. SCHED_RR. This is a round-robbin scheduler. Unlike SCHED_FIFO, the SCHED_RR scheduler does not allow a thread to run indefinitely unless it is preempted by a higher priority thread. If there are multiple threads of the same priority and they are runnable, the scheduler will alow each thread to run for a specific time before it is preempted (quantum).
To start a process with one of the real time schedulers you have to use the system call sched_setscheduler(pid_t pid, int policy, const struct sched_param *p). Once a thread is running under a real time scheduler you can adjust its priority with a call to sched_setparam().
Linux 2.6 Kernel Timers:
nanosleep(): In Linux 2.6 the kernel only checks on timers once every "jiffie" which defaults to 1ms (it was 10ms before 2.6). Even though you can specify times with nanosecond resolution in the call to nanosleep() don't expect that kind of time resolution. nanosleep() is good for a minimum of 2ms - meaning the timer will finish within 2ms of the programmed interval.
If more accurate timing is needed, /dev/rtc can be used. If you do a read() of /dev/rtc it will block until the rtc timer tick is hit. You can program the rtc's tick rate from 2 Hz to 8192 Hz.
Locking memory:
To keep the memory allocated for a process from being swapped out to disk, you can lock all or part of its memory in RAM using the mlock() call. You can unlock the memory by calling munlock(). If you want to lock the entire memory of a process, a call to mlockall(int flags) will lock the entire address space of a process into RAM. The flags are MCL_CURRENT and MCL_FUTURE. MCL_CURRENT will lock the current memory allocated to the process into RAM. MCL_FUTURE will lock any future allocation of memory by the process into physical RAM. You will usually want to or MCL_CURRENT and MCL_FUTURE together.
With the 2.6 kernel, a large step was taken to improve Linux's real-time capabilities. The improvements are a result of two major changes in the kernel (as well as many minor changes):
1. Kernel preemption. Before 2.6 the scheduler was able to preempt threads running in user mode, but when the thread made a system call that caused a context switch to kernel mode there was no way for the thread to be preempted. This situation could cause a high-priority thread that was ready to run to be blocked by a lower priority thread inside a system call. With 2.6, the kernel can now be preempted.
2. Constant time scheduler. Prior to version 2.6, the time that it took for the scheduler to decide which thread to run depended on how many threads that were currently running on the system. The more threads on the system, the more time it took the scheduler to make a decision. In version 2.6, the scheduler makes the decision in the same amount of time whether there are 1 thread or 100 threads on the system. This makes the system more predictable.
Scheduling Priorities:
Most processes are started with the default scheduler: SCHED_NORMAL. There are two other schedulers that can be used for real time threads:
1. SCHED_FIFO. This is a first-in first-out scheduler. The highest priority SCHED_FIFO thread ready to run will be scheduled. Once the thread starts running it will only be preempted if a higher priority real time thread becomes runnable or if the thread yields the processor.
2. SCHED_RR. This is a round-robbin scheduler. Unlike SCHED_FIFO, the SCHED_RR scheduler does not allow a thread to run indefinitely unless it is preempted by a higher priority thread. If there are multiple threads of the same priority and they are runnable, the scheduler will alow each thread to run for a specific time before it is preempted (quantum).
To start a process with one of the real time schedulers you have to use the system call sched_setscheduler(pid_t pid, int policy, const struct sched_param *p). Once a thread is running under a real time scheduler you can adjust its priority with a call to sched_setparam().
Linux 2.6 Kernel Timers:
nanosleep(): In Linux 2.6 the kernel only checks on timers once every "jiffie" which defaults to 1ms (it was 10ms before 2.6). Even though you can specify times with nanosecond resolution in the call to nanosleep() don't expect that kind of time resolution. nanosleep() is good for a minimum of 2ms - meaning the timer will finish within 2ms of the programmed interval.
If more accurate timing is needed, /dev/rtc can be used. If you do a read() of /dev/rtc it will block until the rtc timer tick is hit. You can program the rtc's tick rate from 2 Hz to 8192 Hz.
Locking memory:
To keep the memory allocated for a process from being swapped out to disk, you can lock all or part of its memory in RAM using the mlock() call. You can unlock the memory by calling munlock(). If you want to lock the entire memory of a process, a call to mlockall(int flags) will lock the entire address space of a process into RAM. The flags are MCL_CURRENT and MCL_FUTURE. MCL_CURRENT will lock the current memory allocated to the process into RAM. MCL_FUTURE will lock any future allocation of memory by the process into physical RAM. You will usually want to or MCL_CURRENT and MCL_FUTURE together.
Comments