Comparing real-time scheduling on the Linux kernel and an RTOS

By default, the Linux kernel build used in the many open source distributions is the normal/default kernel which doesn’t support real time scheduling. If an embedded developer wants to compare the scheduling policies of Linux to a real time operating system it is more useful to compare RTOS performance to a version of Linux that does have real-time features.

Fortunately, in addition to this default kernel, there is also available a Real-time kernel version that supports a real-time scheduling policy. In this article and in the code examples that are included, an effort is made to compare the real time operations of standard and real-time Linux with normal RTOS operation and evaluate the differences and similarities.

Normal Linux kernel vs RTOS
Normal Linux Kernel is a preemptive kernel but not real time, of course. In most multithreading environments (also called multitasking ), a preemptive kernel allows the thread that has higher priority to receive longer time on the processor. And, conversely a lower priority thread will have less time with the processor.

However, in the normal kernel, no particular thread can monopolize the services of the resident processor all the time, no matter what its priority. So, programs will never hang up even if an arbitrary thread of the program goes into a “forever loop.”

Conversely, with almost any real time OS (such as FreeRTOS, Micrium uC/OS, and ThreadX ), the kernel supports both preemption and real-time features. This means that a thread (also called a Task ) can run forever when the following conditions are met:  (1) it is not blocked by synchronized resource (I/O block, Mutex, Semaphore…) or (2) it isn’t preempted by threads which may have equal or higher priority.

Figure 1 below shows the differences between the thread scheduling policy of normal Linux and the thread scheduling policy of a RTOS. In Figure 1, we assume that Thread1 has priority 2 and is higher than Thread2 which has priority 1.

In the RTOS scheduler, Thread1, with higher priority, always runs. Thread2 with lower priority never has a chance to run. By comparison the Normal Linux Scheduler does exactly the opposite, bringing up both Thread1 and Thread2 to run. Thread1 with higher priority will have longer time to run as compared with Thread2 which has lower priority.


Click on image to enlarge.

Figure 1. Real Time OS and normal Linux kernel scheduler

Real Time Linux kernel versus RTOS
In a real time kernel version of Linux, the scheduler has three (3) scheduling policies: Normal , FIFO and Round Robin .

In the Normal scheduling policy, a thread will be stopped (suspended) when one of three  conditions occurs:

1 . It is blocked by an accessing synchronize resource (I/O block, mutex, semaphone… )
2. It volunteers to give up control of processor (call sleep() or pthread_yield() ).
3. The Scheduler suspends the thread when its running time exhausted. The running time depends on each thread’s priority, as noted in Figure 1.

Normal real time scheduling policy is same as the default scheduling policy of normal kernel as described earlier.

With the FIFO scheduling policy, a thread will be stopped (suspended) when one of three conditions occurs:

1. It is blocked by accessing synchronize resource (I/O block, mutex, semaphore.. .)
2. It is preempted by a higher priority thread.
3. It volunteers to give up control of processor (call sleep() or pthread_yield() ).

In the Round Robin scheduling policy, a thread will be stopped (suspended) when one in four following conditions occurs:

1. It is blocked by accessing synchronize resource (I/O block, mutex, semaphore… )
2. Or it is preempted by a higher priority thread.
3. Or it volunteers to give up control of processor (call sleep() or pthread_yield() ).
4. Or its Time slice expired.

(Most RTOSes embed Round Robin scheduling policy in their scheduler .)

In Figure 2 below , we have four situations: First, Thread1 and Thread2 are of equal priority and run using the FIFO policy. Second, Thread1 and Thread2 are of equal priority and run on the Round Robin policy. Third, priority of Thread1 is higher priority of Thread2 and they run using the FIFO policy. Fourth, the priority of Thread1 is higher priority of Thread2 and they run using the Round Robin policy.

We also assume that Thread1 starts first, then Thread2 starts later and during running time, Thread1and Thread2 don’t make any system calls or are blocked by any synchronized resource (I/O, Mutex, Semaphore…). The result: the only differences that affect real time performance between the FIFO and Round Robin policies is when Thread1 and Thread2 have same priority.


Click on image to enlarge.

Figure 2. Real time Linux kernel scheduler

Conclusion: Viewed in this context, the typical RTOS scheduler is just a special case of real time Linux scheduler, or in other words, the RTOS scheduler is the real time Linux scheduler running with the Round Robin policy. (Some RTOSes allow configuration of the scheduler to run in a non-preemption mode. However this mode is seldom used. )

The examples included on Page 2 to 8 of this article show the different operational mechanisms of the real time Linux Scheduler in the context of this discussion. To download the source code for these examples, click here .

Important note:   On the following pages, all of the examples are only correct when the program runs in CPU single core and in root permission . To enforce a thread/process so that it only runs in a particular core of the processor, this can be done via the function sched_setaffinity( ) which sets the process CPU affinity. The programs were tested in Real time Linux kernel version 3.0.17.

If you want to install Real Time Linux and test it out yourself, more information is on Page 9, along with some references you may wish to consult.

(Editor’s Note : Any errors in the code listed on the following pages may have been introduced during the conversion process to ready it for online. If so, please contact me directly at bccole@acm.org. )

NORMAL scheduling policy of Linux, Thread1 & Thread2 are not equal priority.

In this policy, Thread1 and Thread2 both have a chance to run although thread1 has higher priority and runs first.  Also, Thread1 with higher priority will get time to run longer than Thread2.

/*
==============================================
Name : Thread_NORMAL.c
Author : Thang Le
Copyright : www.letrungthang.blogspot.com
Description : NORMAL scheduling policy of Linux, Thread1 and Thread2 are not equal priority.
===============================================
*/
#define _GNU_SOURCE
#include
#include
#include

#include
#include

void *print_message_function(void *ptr);

int count1 = 0;
int count2 = 0;
int times = 100000; // need set enough big to see effecting
bool nice_set_1 = 0;
bool nice_set_2 = 0;

//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // for synchronization
when both threads can access to time variable at same time.

int main(void) {
       pthread_t thread1, thread2;
       char *message1 = "Thread 1";
       char *message2 = "Thread 2";
       int iret1, iret2;

                  //Setting CPU affinity
       int num_CPUs = 0; // only run on 1 core
      cpu_set_t mask;

                /* CPU_ZERO initializes all the bits in the mask to zero. */
      CPU_ZERO(&mask);
      /* CPU_SET sets only the bit corresponding to cpu. */
      CPU_SET(num_CPUs, &mask);
      /* sched_setaffinity returns 0 in success */
      if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
      {
                printf("Could not set CPU Affinity n");
      }
      //===========================================

                //set attributes
      pthread_attr_t attr1, attr2;
      struct sched_param parm1, parm2;
      pthread_attr_init(&attr1);
      pthread_attr_init(&attr2);

               //=============================================
     /* Create independent threads each of which will execute function */

              pthread_attr_getschedparam(&attr1, &parm1);
     parm1.sched_priority = sched_get_priority_max(SCHED_OTHER);// no   effect  with this policy,
always return 0
      pthread_attr_setschedpolicy(&attr1, SCHED_OTHER);
     pthread_attr_setschedparam(&attr1, &parm1);

              iret1 = pthread_create(&thread1, &attr1, (void*) print_message_function,

             void*) message1);pthread_setschedparam(thread1, SCHED_OTHER, &parm1);

              //===============================================
     pthread_attr_getschedparam(&attr2, &parm2);
     parm2.sched_priority = sched_get_priority_min(SCHED_OTHER);// no   effect with this policy,
always return 0
     pthread_attr_setschedpolicy(&attr2, SCHED_OTHER);
     pthread_attr_setschedparam(&attr2, &parm2);

             iret2 = pthread_create(&thread2, &attr2, (void*)    print_message_function,
                    (void*) message2);
     pthread_setschedparam(thread2, SCHED_OTHER, &parm2);

            //===============================================

          /* Wait till threads are complete before main continues. Unless we */
   /* wait we run the risk of executing an exit which will terminate */
   /* the process and all threads before the threads have completed. */

         pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

        printf("count1 = %d, count2 = %dn", count1, count2);
    printf("Thread 1 returns: %dn", iret1);
   printf("Thread 2 returns: %dn", iret2);
   exit(0);
   //return EXIT_SUCCESS;
}

//===============================================
void *print_message_function(void *ptr) {
     char *message;
     message = (char *) ptr;

              while (times > 0) {
            int i = 0;
            for (i = 0; i < 20000; i++)
                     i++; // only for delay

                              if (strcmp(message, "Thread 1") == 0) {
                      if(nice_set_1 == 0){
                             nice_set_1 = 1;
                             nice(-20); // set thread1 to highest priority
                      }
                      count1 += 1; // count times Thread1 run

                              } else {
                       if(nice_set_2 == 0) {
                               nice_set_2 = 1;
                               nice(19); // set thread2 to lowest priority
                        }
                        count2 += 1; // count times Thread2 run

                               }

                                //pthread_mutex_lock( &mutex );
             times--;
            //pthread_mutex_unlock( &mutex );
     }

              return (void*) NULL;
}

Result:

count1 = 98563, count2 = 1438
Thread 1 returns: 0
Thread 2 returns: 0

FIFO scheduling policy of Linux, Thread1 priority is higher Thread2 priority.
Thread2 has no chance to run because its priority is lower thread1.

/*
=======================================
Name : Thread_FIFO_1.c
Author : Thang Le
Copyright : www.letrungthang.blogspot.com
Description : FIFO scheduling policy of Linux, Thread1 priority is higher Thread2 priority
========================================
*/
#define _GNU_SOURCE
#include
#include
#include

#include

void *print_message_function(void *ptr);

int count1 = 0;
int count2 = 0;
int times = 100000; // need set enough big to see effecting

int main(void) {
      pthread_t thread1, thread2;
      char *message1 = "Thread 1";
      char *message2 = "Thread 2";
      int iret1, iret2;

                //Setting CPU affinity
      int num_CPUs = 0; // only run on 1 core
      cpu_set_t mask;

              /* CPU_ZERO initializes all the bits in the mask to zero. */
     CPU_ZERO(&mask);
     /* CPU_SET sets only the bit corresponding to cpu. */
     CPU_SET(num_CPUs, &mask);
     /* sched_setaffinity returns 0 in success */
     if(sched_setaffinity(0, sizeof(mask), &mask) == -1)
     {
            printf("Could not set CPU Affinity n");
      }
     //=========================================

        //set attributes
        pthread_attr_t attr1, attr2;
        struct sched_param parm1, parm2;
        pthread_attr_init(&attr1);
        pthread_attr_init(&attr2);

            //======================================
    /* Create independent threads each of which will execute function */

           pthread_attr_getschedparam(&attr1, &parm1);
    parm1.sched_priority = sched_get_priority_max(SCHED_FIFO);
    pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
    pthread_attr_setschedparam(&attr1, &parm1);

           iret1 = pthread_create(&thread1, &attr1, (void*)  print_message_function,
                   (void*) message1);
    pthread_setschedparam(thread1, SCHED_FIFO, &parm1);

//=======================================
    pthread_attr_getschedparam(&attr2, &parm2);
    parm2.sched_priority = sched_get_priority_min(SCHED_FIFO);
    pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
    pthread_attr_setschedparam(&attr2, &parm2);

     iret2 = pthread_create(&thread2, &attr2, (void*) print_message_function,
             (void*) message2);
    pthread_setschedparam(thread2, SCHED_FIFO, &parm2);

         //========================================
   /* Wait till threads are complete before main continues. Unless we */
   /* wait we run the risk of executing an exit which will terminate */
   /* the process and all threads before the threads have completed. */

         pthread_join(thread1, NULL);
   pthread_join(thread2, NULL);printf("count1 = %d, count2 = %dn", count1, count2);
   printf("Thread 1 returns: %dn", iret1);
   printf("Thread 2 returns: %dn", iret2);
   exit(0);
   //return EXIT_SUCCESS;
}

//===========================================
void *print_message_function(void *ptr) {
       char *message;
       message = (char *) ptr;

        while (times > 0) {
             int i = 0;
             for (i = 0; i < 20000; i++)
                i++; // only for delay

                  if (strcmp(message, "Thread 1") == 0) {
                count1 += 1; // count times Thread1 run
       } else {
             count2 += 1; // count times Thread2 run
    }

          times--;
    }

           return (void*) NULL;
}


Result:

count1 = 100000, count2 = 0
Thread 1 returns: 0
Thread 2 returns: 0

 


FIFO scheduling policy of Linux, Thread1 priority is lower Thread2 priority.
Because thread1 starts first,  it runs for a short time before preemption by thread2 which has higher priority. When thread2 runs, thread1 has no chance to run again.

/*
===================================
Name : Thread_FIFO_2.c
Author : Thang Le
Copyright : www.letrungthang.blogspot.com
Description : FIFO scheduling policy of Linux, Thread1 priority is lower Thread2 priority
==================================
*/

#define _GNU_SOURCE
#include
#include
#include

#include

void *print_message_function(void *ptr);

intcount1 = 0;
intcount2 = 0;
inttimes = 100000; // need set enough big to see effecting

intmain(void) {
   pthread_t thread1, thread2;
   char *message1 = "Thread 1";
   char *message2 = "Thread 2";
   int iret1, iret2;

         //Setting CPU affinity
   int num_CPUs = 0; // only run on 1 core
   cpu_set_t mask;

         /* CPU_ZERO initializes all the bits in the mask to zero. */
   CPU_ZERO(&mask);
   /* CPU_SET sets only the bit corresponding to cpu. */
   CPU_SET(num_CPUs, &mask);
   /* sched_setaffinity returns 0 in success */
   if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
   {
       printf("Could not set CPU Affinity n");
   }
   //=======================================

            //set attributes
    pthread_attr_t attr1, attr2;
    struct sched_param parm1, parm2;
    pthread_attr_init(&attr1);
    pthread_attr_init(&attr2);

          //=====================================
    /* Create independent threads each of which will execute function */

           pthread_attr_getschedparam(&attr1, &parm1);
    parm1.sched_priority = sched_get_priority_min(SCHED_FIFO);
    pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
    pthread_attr_setschedparam(&attr1, &parm1);

           iret1 = pthread_create(&thread1, &attr1, (void*)    print_message_function,
                 (void*) message1);
    pthread_setschedparam(thread1, SCHED_FIFO, &parm1);

          //====================================
    pthread_attr_getschedparam(&attr2, &parm2);
    parm2.sched_priority = sched_get_priority_max(SCHED_FIFO);
    pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
    pthread_attr_setschedparam(&attr2, &parm2);

            iret2 = pthread_create(&thread2, &attr2, (void*) print_message_function,
                (void*) message2);
    pthread_setschedparam(thread2, SCHED_FIFO, &parm2);

     //==================================
 /* Wait till threads are complete before main continues. Unless we */
 /* wait we run the risk of executing an exit which will terminate */
 /* the process and all threads before the threads have completed. */
 pthread_join(thread1, NULL);
 pthread_join(thread2, NULL);

    printf("count1 = %d, count2 = %dn", count1, count2);
 printf("Thread 1 returns: %dn", iret1);
 printf("Thread 2 returns: %dn", iret2);
 exit(0);
 //return EXIT_SUCCESS;
}

//======================================
void *print_message_function(void *ptr) {
    char *message;
     message = (char *) ptr;

            while (times > 0) {
          int i = 0;
          for (i = 0; i < 20000; i++)
          i++; // only for delay

                         if (strcmp(message, "Thread 1") == 0) {
                 count1 += 1; // count times Thread1 run
          } else {
                count2 += 1; // count times Thread2 run
}

times--;
}

return (void*) NULL;
}


Result:

count1 = 5444, count2 = 94557
Thread 1 returns: 0
Thread 2 returns: 0

 


FIFO scheduling policy of Linux, Thread1 and Thread2 are equal priority. Thread1 runs first. Because thread1 runs first and no thread has a priority higher than thread1, thread1 runs "forever." This is opposite to the Round-Robin policy. 

/*
====================================
Name : Thread_FIFO_3.c
Author : Thang Le
Copyright : www.letrungthang.blogspot.com
Description : FIFO scheduling policy of Linux, Thread1 and Thread2 are equal priority.
====================================
*/

#define _GNU_SOURCE
#include
#include
#include

#include

void *print_message_function(void *ptr);

int count1 = 0;
int count2 = 0;
int times = 100000; // need set enough big to see effecting

int main(void) {
   pthread_t thread1, thread2;
   char *message1 = "Thread 1";
   char *message2 = "Thread 2";
   int iret1, iret2;

         //Setting CPU affinity
   int num_CPUs = 0; // only run on 1 core
   cpu_set_t mask;

        /* CPU_ZERO initializes all the bits in the mask to zero. */
   CPU_ZERO(&mask);
   /* CPU_SET sets only the bit corresponding to cpu. */
  CPU_SET(num_CPUs, &mask);
  /* sched_setaffinity returns 0 in success */
  if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
  {
       printf("Could not set CPU Affinity n");
  }
  //================================

             //set attributes
     pthread_attr_t attr1, attr2;
     struct sched_param parm1, parm2;
     pthread_attr_init(&attr1);
     pthread_attr_init(&attr2);

         //=============================
   /* Create independent threads each of which will execute function */

         pthread_attr_getschedparam(&attr1, &parm1);
   parm1.sched_priority = sched_get_priority_min(SCHED_FIFO);
   pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
   pthread_attr_setschedparam(&attr1, &parm1);

         iret1 = pthread_create(&thread1, &attr1, (void*) print_message_function,
               (void*) message1);

         pthread_setschedparam(thread1, SCHED_FIFO, &parm1);

       //================================
  pthread_attr_getschedparam(&attr2, &parm2);
  parm2.sched_priority = sched_get_priority_min(SCHED_FIFO);
  pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
  pthread_attr_setschedparam(&attr2, &parm2);

       iret2 = pthread_create(&thread2, &attr2, (void*) print_message_function,
                 (void*) message2);
   pthread_setschedparam(thread2, SCHED_FIFO, &parm2);

      //================================
  /* Wait till threads are complete before main continues. Unless we */
  /* wait we run the risk of executing an exit which will terminate */
  /* the process and all threads before the threads have completed. */

       pthread_join(thread1, NULL);
  pthread_join(thread2, NULL);

       printf("count1 = %d, count2 = %dn", count1, count2);
  printf("Thread 1 returns: %dn", iret1);
  printf("Thread 2 returns: %dn", iret2);
  exit(0);
  //return EXIT_SUCCESS;
}

//=====================================
void *print_message_function(void *ptr) {
    char *message;
    message = (char *) ptr;

            while (times > 0) {
         int i = 0;
         for (i = 0; i < 20000; i++)
             i++; // only for delay

                       if (strcmp(message, "Thread 1") == 0) {
               count1 += 1; // count times Thread1 run
         } else {
               count2 += 1; // count times Thread2 run
    }

            times--;
  }

       return (void*) NULL;
}


Result:

count1 = 100000, count2 = 0
Thread 1 returns: 0
Thread 2 returns: 0

 


Round Robin scheduling policy of Linux, Thread1 priority is higher Thread2 priority. Thread2 has no chance to run because its priority is lower thread1

/*
================================
Name : Thread_RR_1.c
Author : Thang Le
Copyright : www.letrungthang.blogspot.com
Description : Round Robin scheduling policy of Linux, Thread1 priority is higher Thread2 priority.
==================================
*/

#define _GNU_SOURCE
#include
#include
#include

#include

void *print_message_function(void *ptr);

int count1 = 0;
int count2 = 0;
int times = 100000; // need set enough big to see effecting

int main(void) {
   pthread_t thread1, thread2;
   char *message1 = "Thread 1";
   char *message2 = "Thread 2";
   int iret1, iret2;

        //Setting CPU affinity
   int num_CPUs = 0; // only run on 1 core
  cpu_set_t mask;

       /* CPU_ZERO initializes all the bits in the mask to zero. */
  CPU_ZERO(&mask);
  /* CPU_SET sets only the bit corresponding to cpu. */
  CPU_SET(num_CPUs, &mask);
  /* sched_setaffinity returns 0 in success */
  if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
 {
       printf("Could not set CPU Affinity n");
 }
 //==============================

         //set attributes
   pthread_attr_t attr1, attr2;
   struct sched_param parm1, parm2;
   pthread_attr_init(&attr1);
   pthread_attr_init(&attr2);

        //=============================
  /* Create independent threads each of which will execute function */

       pthread_attr_getschedparam(&attr1, &parm1);
  parm1.sched_priority = sched_get_priority_max(SCHED_RR);
  pthread_attr_setschedpolicy(&attr1, SCHED_RR);
  pthread_attr_setschedparam(&attr1, &parm1);
  iret1 = pthread_create(&thread1, &attr1, (void*) print_message_function,
                (void*) message1);
  pthread_setschedparam(thread1, SCHED_RR, &parm1);

      //==============================
 pthread_attr_getschedparam(&attr2, &parm2);
 parm2.sched_priority = sched_get_priority_min(SCHED_RR);              pthread_attr_setschedpolicy(&attr2, SCHED_RR);
 pthread_attr_setschedparam(&attr2, &parm2);

    iret2 = pthread_create(&thread2, &attr2, (void*) print_message_function,
                (void*) message2);
 pthread_setschedparam(thread2, SCHED_RR, &parm2);

      //=============================
 /* Wait till threads are complete before main continues. Unless we */
 /* wait we run the risk of executing an exit which will terminate */
 /* the process and all threads before the threads have completed. */

     pthread_join(thread1, NULL);
 pthread_join(thread2, NULL);

     printf("count1 = %d, count2 = %dn", count1, count2);
 printf("Thread 1 returns: %dn", iret1);
 printf("Thread 2 returns: %dn", iret2);
 exit(0);
 //return EXIT_SUCCESS;
}

//=================================
void *print_message_function(void *ptr) {
   char *message;
   message = (char *) ptr;

    while (times > 0) {
          int i = 0;
          for (i = 0; i < 20000; i++)
                  i++; // only for delay

                         if (strcmp(message, "Thread 1") == 0) {
                  count1 += 1; // count times Thread1 run
          } else {
                 count2 += 1; // count times Thread2 run
          }

                       times--;
    }
    return (void*) NULL;
    }

Result:

count1 = 100000, count2 = 0
Thread 1 returns: 0
Thread 2 returns: 0

Round Robin scheduling policy of Linux, Thread1 priority is lower Thread2 priority. Because thread1 starts first,  it runs a short time before being preempted by thread2 which has higher priority. When thread2 runs, thread1 has no chance to run again.

/*

==================================
Name : Thread_RR_2.c
Author : Thang Le
Copyright : www.letrungthang.blogspot.com
Description : Round Robin scheduling policy of Linux, Thread1 priority is lower Thread2 priority.
=================================
*/

#define _GNU_SOURCE
#include
#include
#include

#include

void *print_message_function(void *ptr);

int count1 = 0;
int count2 = 0;
int times = 100000; // need set enough big to see effecting

int main(void) {
    pthread_t thread1, thread2;
    char *message1 = "Thread 1";
    char *message2 = "Thread 2";
    int iret1, iret2;

          //Setting CPU affinity
   int num_CPUs = 0; // only run on 1 core
   cpu_set_t mask;

         /* CPU_ZERO initializes all the bits in the mask to zero. */
   CPU_ZERO(&mask);
   /* CPU_SET sets only the bit corresponding to cpu. */
   CPU_SET(num_CPUs, &mask);
   /* sched_setaffinity returns 0 in success */
   if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
   {
         printf("Could not set CPU Affinity n");
   }
  //===============================

      //set attributes
  pthread_attr_t attr1, attr2;
  struct sched_param parm1, parm2;
  pthread_attr_init(&attr1);
  pthread_attr_init(&attr2);

      //=============================
 /* Create independent threads each of which will execute function */

     pthread_attr_getschedparam(&attr1, &parm1);
  parm1.sched_priority = sched_get_priority_min(SCHED_RR);
 pthread_attr_setschedpolicy(&attr1, SCHED_RR);
 pthread_attr_setschedparam(&attr1, &parm1);

    iret1 = pthread_create(&thread1, &attr1, (void*) print_message_function,
                 (void*) message1);
 pthread_setschedparam(thread1, SCHED_RR, &parm1);

    //============================
 pthread_attr_getschedparam(&attr2, &parm2);
 parm2.sched_priority = sched_get_priority_max(SCHED_RR);
 pthread_attr_setschedpolicy(&attr2, SCHED_RR);
 pthread_attr_setschedparam(&attr2, &parm2);

     iret2 = pthread_create(&thread2, &attr2, (void*) print_message_function,
                (void*) message2);
 pthread_setschedparam(thread2, SCHED_RR, &parm2);

    //===========================
 /* Wait till threads are complete before main continues. Unless we */
 /* wait we run the risk of executing an exit which will terminate */
 /* the process and all threads before the threads have completed. */

     pthread_join(thread1, NULL);
 pthread_join(thread2, NULL);

     printf("count1 = %d, count2 = %dn", count1, count2);
  printf("Thread 1 returns: %dn", iret1);
 printf("Thread 2 returns: %dn", iret2);
 exit(0);
 //return EXIT_SUCCESS;
}
//================================
void *print_message_function(void *ptr) {
       char *message;
       message = (char *) ptr;

                  while (times > 0) {
             int i = 0;

             for (i = 0; i < 20000; i++)

                                 i++; // only for delay

                                if (strcmp(message, "Thread 1") == 0) {
                    count1 += 1; // count times Thread1 run
             } else {
                    count2 += 1; // count times Thread2 run
       }

                times--;
 }

    return (void*) NULL;
}


Result:

count1 = 5809, count2 = 94192
Thread 1 returns: 0
Thread 2 returns: 0

Round Robin scheduling policy of Linux, Thread1 & Thread2 of equal priority. Thread1 runs first. Both thread1 and thread2 have the same chance to run. This is opposite to the FIFO policy. There is a slight difference between value of count1 and count2, because the time to execute the program in thread1 might slightly differ from thread2.

/*
javascript:void(0);===============================
Name : Thread_RR_3.c
Author : Thang Le
Copyright : www.letrungthang.blogspot.com
Description : Round Robin scheduling policy of Linux, Thread1 and Thread2 are equal priority. Thread1 runs first.
==============================
*/

#define _GNU_SOURCE
#include
#include
#include

#include

void *print_message_function(void *ptr);

int count1 = 0;
int count2 = 0;
int times = 100000; // need set enough big to see effecting

int main(void) {
     pthread_t thread1, thread2;
     char *message1 = "Thread 1";
     char *message2 = "Thread 2";
     int iret1, iret2;

             //Setting CPU affinity
     int num_CPUs = 0; // only run on 1 core
     cpu_set_t mask;

            /* CPU_ZERO initializes all the bits in the mask to zero. */
    CPU_ZERO(&mask);
    /* CPU_SET sets only the bit corresponding to cpu. */
    CPU_SET(num_CPUs, &mask);
    /* sched_setaffinity returns 0 in success */
    if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
    {
          printf("Could not set CPU Affinity n");
    }
    //==============================

                  //set attributes
       pthread_attr_t attr1, attr2;
       struct sched_param parm1, parm2;
       pthread_attr_init(&attr1);
       pthread_attr_init(&attr2);

                  //===============================

                 /* Create independent threads each of which will execute function */

                pthread_attr_getschedparam(&attr1, &parm1);
      parm1.sched_priority = sched_get_priority_min(SCHED_RR);
      pthread_attr_setschedpolicy(&attr1, SCHED_RR);
      pthread_attr_setschedparam(&attr1, &parm1);
      iret1 = pthread_create(&thread1, &attr1, (void*)   print_message_function,
                     (void*) message1);
      pthread_setschedparam(thread1, SCHED_RR, &parm1);

               //===============================
      pthread_attr_getschedparam(&attr2, &parm2);
      parm2.sched_priority = sched_get_priority_min(SCHED_RR);
      pthread_attr_setschedpolicy(&attr2, SCHED_RR);
      pthread_attr_setschedparam(&attr2, &parm2);
      iret2 = pthread_create(&thread2, &attr2, (void*) print_message_function,

                                               (void*) message2);
      pthread_setschedparam(thread2, SCHED_RR, &parm2);

               //===============================
     /* Wait till threads are complete before main continues. Unless we */
     /* wait we run the risk of executing an exit which will terminate */
     /* the process and all threads before the threads have completed. */

              pthread_join(thread1, NULL);
     pthread_join(thread2, NULL);

              printf("count1 = %d, count2 = %dn", count1, count2);
     printf("Thread 1 returns: %dn", iret1);
     printf("Thread 2 returns: %dn", iret2);
     exit(0);
     //return EXIT_SUCCESS;
}

//=====================================
void *print_message_function(void *ptr) {
      char *message;
      message = (char *) ptr;

                while (times > 0) {
              int i = 0;
              for (i = 0; i < 20000; i++)
                      i++; // only for delay

                                   if (strcmp(message, "Thread 1") == 0) {
                      count1 += 1; // count times Thread1 run
              } else {
                      count2 += 1; // count times Thread2 run
              }
              times--;
      }

                return (void*) NULL;
}


Result:

count1 = 48936, count2 = 51065
Thread 1 returns: 0
Thread 2 returns: 0

How to install Real Time Linux Kernel
In Fedora Linux, the real-time kernel can be obtained at the Planet CCRMA at Home software repositories (Figure 3, below ).


Clickon image to enlarge.

Figure 3. Real time kernel installation

You can install the real-time kernel, along with other system optimizations, by following these instructions:

1. Install the Planet CCRMA at Home repositories by entering the following:

rpm -Uvh

http://ccrma.stanford.edu/planetccrma/mirror/fedora/linux/planetccrma/15/i386/planetccrma-repo-1.1-3.fc15.ccrma.noarch.rpm

2. Run the following terminal command line: 

su -c 'yum install planetccrma-

Note that this is a meta-package, which does not install anything by itself, but causes a number of other packages to be installed, which will themselves perform the desired installation and optimization.

3. To test the new kernel, shut down and reboot your computer. If you decided to modify your GRUB configuration, be sure that you leave a non-real-time kernel available for use.

Le Trung Thang has a BS in Electronics Engineering at Ho Chi Minh National University, Vietnam, and is currently working as an embedded software engineer in Singapore. He has five years experiences in software development, specific to embedded system. He is active in many projects with variant languages from C/C++ to Java and maintains a blog and can be contacted at .

(Editor’s Note to readers: Any errors in the code listed on the preceding pages may have been introduced during the conversion process to ready it for online. If so, please contact me directly at bccole@acm.org. )

To download the source code , click here .

References:
1- Linux Kernel Development - Robert Love - Addison Wesley 2010.
2 - Using the FreeRTOS Real Time Kernel - A Practical Guide - 2009 Richard Barry (www.freertos.org ).
3 - uCOS-III The Real-Time Kernel, Jean Labrosse, Micrium Press 2011

1 thought on “Comparing real-time scheduling on the Linux kernel and an RTOS

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.