Tag Archives: rt-linux

Parallel RT Processes with Same Periodic Cycles

For many control applications, multiple RT processes need to run with periodic cycles.

This article presents an example code that enables two RT applications to run with a same periodic cycle. Two RT processes share one shared memory to pass the reference start time of the thread.

Once two processes are synchronized, then two processes control their cycles with “CLOCK_MONOTONIC” clock.

The code for the first app code

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include "shared_memory_base.h"

#define TIMESPEC_ADD(A,B) /* A += B */ \
    do {                                   \
    (A).tv_sec += (B).tv_sec;          \
    (A).tv_nsec += (B).tv_nsec;        \
    if ( (A).tv_nsec >= 1000000000 ) { \
    (A).tv_sec++;                  \
    (A).tv_nsec -= 1000000000;     \
    }                                  \
    } while (0)

#define TIMESPEC_SUB(A,B) /* A -= B */ \
    do {                                   \
    (A).tv_sec -= (B).tv_sec;          \
    (A).tv_nsec -= (B).tv_nsec;        \
    if ( (A).tv_nsec < 0 ) {           \
    (A).tv_sec--;                  \
    (A).tv_nsec += 1000000000;     \
    }                                  \
    } while (0)


void *thread_func ( void *param )
{
    FILE *fp;
    char filename_fp[50];
    shared_memory_base comm;
    comm.init();
    comm.data->first_app_on=1;

    struct timespec t_1us;
    t_1us.tv_sec = 0; t_1us.tv_nsec=1000;


    long thread_id = (long) param;

    sprintf (filename_fp, "%ld%s", thread_id, ".txt");
    fp = fopen(filename_fp, "w");//opening file

    struct timespec t_next, period, t_now, t_prev, t_diff;

    /* period = 0.5 ms * thread_id */
    period.tv_sec = 0;
    period.tv_nsec = ( 1 ) * 500000; // a x ms

    comm.data->first_app_on = 1;
    while (comm.data->second_app_on == 0);
    clock_nanosleep ( CLOCK_MONOTONIC, 0, &t_1us, NULL );   // wait 1us for second app till it set up the t_ref
    t_now = comm.data->t_ref;
    t_next = t_now;
    t_prev = t_now;

    int t_jitter[100];
    for (int i=0;i<100;i++) t_jitter[i] = 0;
    int t_j = 0;

    for ( int i = 0; i < 10000; i++ )
    {
        clock_gettime ( CLOCK_MONOTONIC, &t_now );
        t_diff = t_now;
        TIMESPEC_SUB ( t_diff, t_prev );
        t_prev = t_now;
        t_j = t_diff.tv_nsec / 10000;
        if (t_j<0) t_jitter[0]++;
        else if (t_j>99) t_jitter[99]++;
        else t_jitter[t_j]++;
        if(i%1000==1) printf("second: %d\n",i);
        TIMESPEC_ADD ( t_next, period );
        clock_nanosleep ( CLOCK_MONOTONIC, TIMER_ABSTIME, &t_next, NULL );
    }

    for (int i=0;i<100;i++) {
        printf("%d ",t_jitter[i]);
        fprintf(fp, "%d\n",t_jitter[i]);
    }
    fclose(fp);
    comm.detach_shared_memory();
    return NULL;
}

int main ()
{
    int policy;
    struct sched_param prio;
    pthread_attr_t attr;

    pthread_t tid1;

    policy = SCHED_OTHER;
    if (pthread_setschedparam( pthread_self(),policy, &prio )){
        perror ("Error: pthread_setschedparam (root permission?)");
        exit(1);
    }

    pthread_attr_init( &attr);
    pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED);
    policy = SCHED_RR;
    pthread_attr_setschedpolicy( &attr, policy);
    prio.sched_priority = 1; // priority range should be btw -20 to +19
    pthread_attr_setschedparam(&attr,&prio);

    if ( pthread_create(&tid1, &attr, thread_func, (void *)(1)) ){
        perror ( "Error: pthread1_create" );
        return 1;
    }

    /* wait for threads to finish */
    pthread_join ( tid1, NULL );

    return 0;
}

The code for the second application

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include "shared_memory_base.h"

#define TIMESPEC_ADD(A,B) /* A += B */ \
    do {                                   \
    (A).tv_sec += (B).tv_sec;          \
    (A).tv_nsec += (B).tv_nsec;        \
    if ( (A).tv_nsec >= 1000000000 ) { \
    (A).tv_sec++;                  \
    (A).tv_nsec -= 1000000000;     \
    }                                  \
    } while (0)

#define TIMESPEC_SUB(A,B) /* A -= B */ \
    do {                                   \
    (A).tv_sec -= (B).tv_sec;          \
    (A).tv_nsec -= (B).tv_nsec;        \
    if ( (A).tv_nsec < 0 ) {           \
    (A).tv_sec--;                  \
    (A).tv_nsec += 1000000000;     \
    }                                  \
    } while (0)


void *thread_func ( void *param )
{
    FILE *fp;
    char filename_fp[50];
    shared_memory_base comm;
    comm.init();    // start the shared memory communication

    // get the file name
    long thread_id = (long) param;
    sprintf (filename_fp, "%ld%s", thread_id, ".txt");
    fp = fopen(filename_fp, "w");//opening file

    struct timespec t_next, period, t_now, t_prev, t_diff;

    /* period = 0.5 ms * thread_id */
    period.tv_sec = 0;
    period.tv_nsec = ( 1 ) * 500000; // a x ms

    comm.data->second_app_on = 1; // let the second app go
    while( comm.data->first_app_on == 0 );
    clock_gettime ( CLOCK_MONOTONIC, &t_now );
    comm.data->t_ref = t_now;
    t_next = t_now;
    t_prev = t_now;

    int t_jitter[100];
    for (int i=0;i<100;i++) t_jitter[i] = 0;
    int t_j = 0;


    for ( int i = 0; i < 10000; i++ )
    {
        clock_gettime ( CLOCK_MONOTONIC, &t_now );
        t_diff = t_now;
        TIMESPEC_SUB ( t_diff, t_prev );
        t_prev = t_now;
        t_j = t_diff.tv_nsec / 10000;
        if (t_j<0) t_jitter[0]++;
        else if (t_j>99) t_jitter[99]++;
        else t_jitter[t_j]++;
        if(i%1000==1) printf("first: %d\n",i);
        TIMESPEC_ADD ( t_next, period );
        clock_nanosleep ( CLOCK_MONOTONIC, TIMER_ABSTIME, &t_next, NULL );
    }

    for (int i=0;i<100;i++) {
        printf("%d ",t_jitter[i]);
        fprintf(fp, "%d\n",t_jitter[i]);
    }
    fclose(fp);
    comm.detach_shared_memory();
    return NULL;
}

int main ()
{
    int policy;
    struct sched_param prio;
    pthread_attr_t attr;

    pthread_t tid1;

    policy = SCHED_OTHER;
    if (pthread_setschedparam( pthread_self(),policy, &prio )){
        perror ("Error: pthread_setschedparam (root permission?)");
        exit(1);
    }

    pthread_attr_init( &attr);
    pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED);
    policy = SCHED_RR;
    pthread_attr_setschedpolicy( &attr, policy);
    prio.sched_priority = 1; // priority range should be btw -20 to +19
    pthread_attr_setschedparam(&attr,&prio);

    if ( pthread_create(&tid1, &attr, thread_func, (void *)(2)) ){
        perror ( "Error: pthread1_create" );
        return 1;
    }

    /* wait for threads to finish */
    pthread_join ( tid1, NULL );

    return 0;
}

The result of the execution of two programs

mok@mok-master-s:~/dev$ mkdir build
 mok@mok-master-s:~/dev$ cd build
 mok@mok-master-s:~/dev/build$ cmake ../rt_periodic_thread
 -- The CXX compiler identification is GNU 7.4.0
 -- Check for working CXX compiler: /usr/bin/c++
 -- Check for working CXX compiler: /usr/bin/c++ -- works
 -- Detecting CXX compiler ABI info
 -- Detecting CXX compiler ABI info - done
 -- Detecting CXX compile features
 -- Detecting CXX compile features - done
 -- Configuring done
 -- Generating done
 -- Build files have been written to: /home/mok/dev/build
 mok@mok-master-s:~/dev/build$ make
 Scanning dependencies of target first_app
 [ 16%] Building CXX object CMakeFiles/first_app.dir/first_rt_app.cpp.o
 [ 33%] Building CXX object CMakeFiles/first_app.dir/shared_memory_base.cpp.o
 [ 50%] Linking CXX executable first_app
 [ 50%] Built target first_app
 Scanning dependencies of target second_app
 [ 66%] Building CXX object CMakeFiles/second_app.dir/second_rt_app.cpp.o
 [ 83%] Building CXX object CMakeFiles/second_app.dir/shared_memory_base.cpp.o
 [100%] Linking CXX executable second_app
 [100%] Built target second_app
 mok@mok-master-s:~/dev/build$ mv ../rt_periodic_thread/runboth.sh ./
 mok@mok-master-s:~/dev/build$ chmod a+x runboth.sh 
 mok@mok-master-s:~/dev/build$ sudo ./runboth.sh 
 [sudo] password for mok: 
 second: 1
 first: 1
 second: 1001
 first: 1001
 second: 2001
 first: 2001
 first: 3001
 second: 3001
 second: 4001
 first: 4001
 first: 5001
 second: 5001
 first: 6001
 second: 6001
 first: 7001
 second: 7001
 second: 8001
 first: 8001
 second: 9001
 first: 9001
 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 60 2065 675 2339 2058 573 2187 32 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 7 40 2179 518 2484 2008 449 2269 37 5 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 mok@mok-master-s:~/dev/build$ ^C

Download and Build

https://drive.google.com/file/d/1kTSBW3HqnMccRxj6cudEWy5nLYXzAuuY/view?usp=sharing

You can build and run the two applications with the command below:

mkdir build
cd build
cmake ../rt_periodic_thread
make
mv ../rt_periodic_thread/runboth.sh ./
chmod a+x runboth.sh 
sudo ./runboth.sh 

Other Topics

Tutorial: How to make RT Preempt Linux with Ubuntu 18.04.02

In this tutorial, I will present how to make a RT Preempt Linux with Ubuntu 18.04.02

1. install Ubuntu 18.04.02 in your computer. Let me skip the details since there are so many other tutorials for this.

2. Once the install is finished. Download Linux kernel and RT Patch

  • Linux Kernel Download at

https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/

Screenshot from 2020-02-20 23-20-00

  • RT patch download at https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/4.4/

Screenshot from 2020-02-20 23-21-54

3. Prepare to compile the kernel

cd Downloads/
xz -cd linux-4.4.208.tar.xz | tar xvf -
cd linux-4.4.208/
xzcat ../patch-4.4.208-rt191.patch.xz | patch -p1
sudo apt install libncurses5-dev build-essential cmake libssl-dev git flex bison
make menuconfig
  • Set “High Resulution Timer Support” Screenshot from 2020-02-21 00-08-07 Screenshot from 2020-02-21 00-08-13
  • Set “Fully Preemptible Kernel (RT)

Screenshot from 2020-02-21 00-08-47 Screenshot from 2020-02-21 00-09-26 Screenshot from 2020-02-21 00-09-43

  • (optional) set Local version nameScreenshot from 2020-02-21 00-10-24 Screenshot from 2020-02-21 00-10-28
  • Save the config fileScreenshot from 2020-02-21 00-11-08

4. Compile and install the kernel ( WARNING: It takes very long time)

make -j20
sudo make modules_install -j20
sudo make install -j20

Here -jx is to specify the number of CPU cores, if you just choose a high number, it go with your all CPU cores.

5. Grub setting

sudo gedit /etc/default/grub

Then, edit the text as below:

To select the booting option in GRUB

GRUB_TIMEOUT_STYLE=hidden -> GRUB_TIMEOUT_STYLE=menu

To increase the time out for selecting a menu

GRUB_TIMEOUT=0 -> GRUB_TIMEOUT=3

To reduce the visual effect

GRUB_CMDLINE_LINUX_DEFAULT=”quiet splash” -> GRUB_CMDLINE_LINUX_DEFAULT=”quiet”

If you want to boot without graphical desktop (terminal mode)

GRUB_CMDLINE_LINUX=”” -> GRUB_CMDLINE_LINUX=”3″

5. Rebooting

sudo update-grub
sudo reboot

6. Select RT-preempt kernel in GRUB

select Advanced and RT-Linux

(Optional)  If you want to customize GRUB more, then use grub-customizer (grub editor with GUI)

sudo add-apt-repository ppa:danielrichter2007/grub-customizer 
sudo apt update
sudo apt install grub-customizer

Other readings

Now you are ready to develop or run an RT application on RT Preempt Linux. If you need to know how to write a RT thread running with a periodic cycle, read another article of mine.

Good luck!