Linux has several scheduling policies that it applies to application processes and threads. SCHED_OTHER is the default Linux time-sharing policy. It is intended for all threads and does not provide real-time mechanisms.
In our application, we use another policy, SCHED_FIFO. This is a simple scheduling algorithm. All threads that use this scheduler can only be preempted by a thread with a higher priority. If the thread goes to sleep, it is placed at the back of the queue of those threads with the same priority.
The priority of a thread with a SCHED_FIFO policy is always higher than the priority of any thread with a SCHED_OTHER policy, and as soon as a SCHED_FIFO thread becomes runnable, it immediately preempts a running SCHED_OTHER thread. From a practical standpoint, if there is only one SCHED_FIFO thread running in the system, it can use as much CPU time as it requires. The deterministic behavior and high priority of the SCHED_FIFO scheduler make it a good fit for real-time applications.
To assign a real-time priority to a thread, we define a ConfigureRealtime function. This accepts two parameters—a thread ID and the desired priority:
void ConfigureRealtime(pthread_t thread_id, int priority) {
The function populates data for the pthread_setschedparam function that uses the low-level API of the operating system to change the scheduler and the priority of a thread:
if (pthread_setschedparam(thread_id,
SCHED_FIFO, &sch)) {
We define a Measure function that runs a busy loop, invoking a nanosleep function with parameters requiring it to sleep for 10 nanoseconds – way too short to yield execution to another thread:
struct timespec delay{0, 10};
for (int i = 0; i < 100000; i++) {
nanosleep(&delay, nullptr);
}
This function captures timestamps before and after the loop and calculates the elapsed time in seconds:
struct timespec ts;
timespec_get(&ts, TIME_UTC);
double delta = (ts.tv_sec - prev.tv_sec) +
(double)(ts.tv_nsec - prev.tv_nsec) / 1000000000;
Next, we define the RealTimeThread function as a wrapper around the Measure function. This sets the priority of the current thread to real time and immediately invokes Measure:
ConfigureRealtime(pthread_self(), 1);
Measure(txt);
In the main function, we start two threads, passing text literals as parameters to differentiate their output. If we run the program on a Raspberry Pi device, we can see the following output:
Real-time threads took four times lesser time because this was not preempted by normal threads. This technique can be efficiently used to meet the soft real-time requirements in the Linux environment.