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
Like this:
Like Loading...