Example
From ChronOS Linux
(Difference between revisions)
| (2 intermediate revisions not shown) | |||
| Line 1: | Line 1: | ||
| - | + | This illustrates the fundamental idea/schema to write a real-time periodic task in ChronOS. | |
| + | |||
| + | You will learn how to write a real-time task (locking the memory, i.e. do not allow the OS to swap the application memory) pin a task to a cpu (affinity mask).. all within ChronOS?! | ||
<code> | <code> | ||
| Line 10: | Line 12: | ||
* This version is terrible system with just one periodic thread | * This version is terrible system with just one periodic thread | ||
*/ | */ | ||
| - | + | ||
#include <pthread.h> | #include <pthread.h> | ||
#include <sys/types.h> | #include <sys/types.h> | ||
#include <unistd.h> | #include <unistd.h> | ||
#include "libchronos/chronos.h" | #include "libchronos/chronos.h" | ||
| - | + | ||
/* PARAMETERS */ | /* PARAMETERS */ | ||
#define SET_SCHED_ALGO SCHED_RT_RMA | #define SET_SCHED_ALGO SCHED_RT_RMA | ||
| - | //#define SET_SCHED_FLAG | + | //#define SET_SCHED_FLAG (SCHED_FLAG_PI | SCHED_FLAG_HUA | SCHED_FLAG_NO_DEADLOCKS) |
#define SET_SCHED_FLAG 0 | #define SET_SCHED_FLAG 0 | ||
#define SET_SCHED_PRIO ((SET_SCHED_ALGO & SCHED_GLOBAL_MASK) ? TASK_RUN_PRIO : -1) | #define SET_SCHED_PRIO ((SET_SCHED_ALGO & SCHED_GLOBAL_MASK) ? TASK_RUN_PRIO : -1) | ||
#define SET_SCHED_PROC 0 | #define SET_SCHED_PROC 0 | ||
| - | + | ||
#define UTIL 50 | #define UTIL 50 | ||
#define PERIOD 1000000 | #define PERIOD 1000000 | ||
| - | #define EXEC | + | #define EXEC 50 |
| - | + | ||
#define JOBS_NUM 32 | #define JOBS_NUM 32 | ||
#define ITER_NUM 4096 | #define ITER_NUM 4096 | ||
| - | + | ||
/* macros to set or zero cpus in a cpu mask */ | /* macros to set or zero cpus in a cpu mask */ | ||
#define MASK_ZERO(mask) (mask) = 0 | #define MASK_ZERO(mask) (mask) = 0 | ||
#define MASK_SET(mask, cpu) (mask) |= (unsigned long) pow(2, cpu) | #define MASK_SET(mask, cpu) (mask) |= (unsigned long) pow(2, cpu) | ||
| - | + | ||
/* TASK_xxx_PRIO */ | /* TASK_xxx_PRIO */ | ||
#define MAIN_PRIO 98 | #define MAIN_PRIO 98 | ||
| Line 40: | Line 42: | ||
#define TASK_CLEANUP_PRIO 92 | #define TASK_CLEANUP_PRIO 92 | ||
#define TASK_RUN_PRIO 90 | #define TASK_RUN_PRIO 90 | ||
| - | + | ||
/* Numerical definitions */ | /* Numerical definitions */ | ||
#define THOUSAND 1000 | #define THOUSAND 1000 | ||
#define MILLION 1000000 | #define MILLION 1000000 | ||
#define BILLION 1000000000 | #define BILLION 1000000000 | ||
| - | + | ||
static pthread_attr_t attr; | static pthread_attr_t attr; | ||
static struct sched_param tparam; | static struct sched_param tparam; | ||
| - | + | ||
static inline long long timespec_subtract_us(struct timespec *x, | static inline long long timespec_subtract_us(struct timespec *x, | ||
struct timespec *y) | struct timespec *y) | ||
| Line 59: | Line 61: | ||
sec--; | sec--; | ||
} | } | ||
| - | + | ||
return (sec * MILLION) + (nsec / THOUSAND); | return (sec * MILLION) + (nsec / THOUSAND); | ||
} | } | ||
| - | + | ||
static void find_job_deadline(int job, long period, struct timespec * start_time, struct timespec *deadline) | static void find_job_deadline(int job, long period, struct timespec * start_time, struct timespec *deadline) | ||
{ | { | ||
unsigned long long nsec, carry; | unsigned long long nsec, carry; | ||
unsigned long offset = (job + 1) * period; //the offset from the start time this deadline is | unsigned long offset = (job + 1) * period; //the offset from the start time this deadline is | ||
| - | + | ||
nsec = start_time->tv_nsec + offset * THOUSAND; | nsec = start_time->tv_nsec + offset * THOUSAND; | ||
carry = nsec / BILLION; | carry = nsec / BILLION; | ||
| - | + | ||
deadline->tv_nsec = nsec % BILLION; | deadline->tv_nsec = nsec % BILLION; | ||
deadline->tv_sec = start_time->tv_sec + carry; | deadline->tv_sec = start_time->tv_sec + carry; | ||
} | } | ||
| - | + | ||
void *start_task(void *arg) | void *start_task(void *arg) | ||
{ | { | ||
| Line 82: | Line 84: | ||
struct sched_param param; | struct sched_param param; | ||
struct timespec start_time, end_time, deadline, release, tperiod; | struct timespec start_time, end_time, deadline, release, tperiod; | ||
| - | + | ||
tperiod.tv_sec = PERIOD / MILLION; | tperiod.tv_sec = PERIOD / MILLION; | ||
tperiod.tv_nsec = (PERIOD % MILLION) * THOUSAND; | tperiod.tv_nsec = (PERIOD % MILLION) * THOUSAND; | ||
| - | + | ||
| - | + | ||
pid_t tid, pid; | pid_t tid, pid; | ||
long long tardiness; | long long tardiness; | ||
unsigned long thread_mask = SET_SCHED_PROC; | unsigned long thread_mask = SET_SCHED_PROC; | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
//set the affinity of this thread to whatever was specified in the taskset file | //set the affinity of this thread to whatever was specified in the taskset file | ||
if(sched_setaffinity(0, sizeof(thread_mask), (cpu_set_t *) &thread_mask)) { | if(sched_setaffinity(0, sizeof(thread_mask), (cpu_set_t *) &thread_mask)) { | ||
| Line 100: | Line 97: | ||
pthread_exit(0); | pthread_exit(0); | ||
} | } | ||
| - | + | ||
//increase priority to TASK_START_PRIO | //increase priority to TASK_START_PRIO | ||
param.sched_priority = TASK_START_PRIO; | param.sched_priority = TASK_START_PRIO; | ||
pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); | pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); | ||
| - | + | ||
//get the start time and CAS on the start-time so all the threads agree on when they got here | //get the start time and CAS on the start-time so all the threads agree on when they got here | ||
clock_gettime(CLOCK_REALTIME, &start_time); | clock_gettime(CLOCK_REALTIME, &start_time); | ||
| - | + | ||
| - | // periodic LOOP | + | // periodic LOOP |
| - | for (job = 0; job<JOBS_NUM; job++) { | + | for (job = 0; job < JOBS_NUM; job++) { |
find_job_deadline(job, period, &start_time, &deadline); //find the deadline for this taskset | find_job_deadline(job, period, &start_time, &deadline); //find the deadline for this taskset | ||
clock_gettime(CLOCK_REALTIME, &release); // real release time | clock_gettime(CLOCK_REALTIME, &release); // real release time | ||
| - | + | ||
//enter real-time segment | //enter real-time segment | ||
| - | |||
| - | |||
begin_rtseg_self(TASK_RUN_PRIO, UTIL, &deadline, &tperiod, EXEC); | begin_rtseg_self(TASK_RUN_PRIO, UTIL, &deadline, &tperiod, EXEC); | ||
| - | + | ||
// DO SOMETHING like a printf... :-) | // DO SOMETHING like a printf... :-) | ||
printf("job %d\n", job); | printf("job %d\n", job); | ||
l = 123456789; | l = 123456789; | ||
| - | for (i=0; i< ITER_NUM; i++) { | + | for (i = 0; i < ITER_NUM; i++) { |
l *= i; | l *= i; | ||
// or nop | // or nop | ||
} | } | ||
i = l; | i = l; | ||
| - | + | ||
//end real time segment | //end real time segment | ||
clock_gettime(CLOCK_REALTIME, &end_time); //get the endtime | clock_gettime(CLOCK_REALTIME, &end_time); //get the endtime | ||
end_rtseg_self(TASK_CLEANUP_PRIO); //end the real-time segment | end_rtseg_self(TASK_CLEANUP_PRIO); //end the real-time segment | ||
| - | + | ||
tardiness = timespec_subtract_us(&deadline, &end_time); //calculate tardiness from deadline and endtime | tardiness = timespec_subtract_us(&deadline, &end_time); //calculate tardiness from deadline and endtime | ||
| - | + | ||
if (tardiness >= 0) { //otherwise, did we meet our deadline? | if (tardiness >= 0) { //otherwise, did we meet our deadline? | ||
//sleep for however much time remains before the next release of this task | //sleep for however much time remains before the next release of this task | ||
| Line 139: | Line 134: | ||
} | } | ||
} | } | ||
| - | + | ||
pthread_exit(NULL); | pthread_exit(NULL); | ||
} | } | ||
| - | + | ||
int main(int argc, char* argv[]) { | int main(int argc, char* argv[]) { | ||
| - | + | ||
int i; | int i; | ||
struct sched_param param, old_param; | struct sched_param param, old_param; | ||
unsigned long main_mask = 0; | unsigned long main_mask = 0; | ||
pthread_t thread; | pthread_t thread; | ||
| - | + | ||
//TODO we have to put mem lock all and mem unlock all at the end | //TODO we have to put mem lock all and mem unlock all at the end | ||
| - | + | ||
//set outselves as a real-time task | //set outselves as a real-time task | ||
sched_getparam(0, &old_param); | sched_getparam(0, &old_param); | ||
| Line 159: | Line 154: | ||
return 0; | return 0; | ||
} | } | ||
| - | + | ||
//set task affinity of this main thread to the first processor | //set task affinity of this main thread to the first processor | ||
MASK_ZERO(main_mask); | MASK_ZERO(main_mask); | ||
MASK_SET(main_mask, 0); | MASK_SET(main_mask, 0); | ||
| - | if (sched_setaffinity(0, sizeof(main_mask), (cpu_set_t *) & main_mask) | + | if (sched_setaffinity(0, sizeof(main_mask), (cpu_set_t *) & main_mask) < 0) { |
| - | + | ||
printf("sched_setaffinity() failed."); | printf("sched_setaffinity() failed."); | ||
return 0; | return 0; | ||
} | } | ||
| - | + | ||
if (set_scheduler(SET_SCHED_ALGO | SET_SCHED_FLAG, SET_SCHED_PRIO, SET_SCHED_PROC)) { | if (set_scheduler(SET_SCHED_ALGO | SET_SCHED_FLAG, SET_SCHED_PRIO, SET_SCHED_PROC)) { | ||
printf("Selection of RT scheduler failed! Is the scheduler loaded?"); | printf("Selection of RT scheduler failed! Is the scheduler loaded?"); | ||
return 0; | return 0; | ||
} | } | ||
| - | + | ||
tparam.sched_priority = TASK_CREATE_PRIO; | tparam.sched_priority = TASK_CREATE_PRIO; | ||
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); | pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); | ||
pthread_attr_setschedpolicy(&attr, SCHED_FIFO); | pthread_attr_setschedpolicy(&attr, SCHED_FIFO); | ||
pthread_attr_setschedparam(&attr, &tparam); | pthread_attr_setschedparam(&attr, &tparam); | ||
| - | + | ||
if (pthread_create(thread, &attr, start_task,0)) { | if (pthread_create(thread, &attr, start_task,0)) { | ||
printf("Failed to pthread_create one of the task threads."); | printf("Failed to pthread_create one of the task threads."); | ||
| Line 187: | Line 181: | ||
return 0; | return 0; | ||
} | } | ||
| - | + | ||
//return us to a normal scheduler and priority | //return us to a normal scheduler and priority | ||
sched_setscheduler(0, SCHED_OTHER, &old_param); | sched_setscheduler(0, SCHED_OTHER, &old_param); | ||
Current revision as of 18:41, 18 April 2014
This illustrates the fundamental idea/schema to write a real-time periodic task in ChronOS.
You will learn how to write a real-time task (locking the memory, i.e. do not allow the OS to swap the application memory) pin a task to a cpu (affinity mask).. all within ChronOS?!
/*
* Antonio Barbalace
* periodic segments benchmark
* this work was done in the integration phase of the AUV code with ChronOS
*
* The whole code derive from the sched_test_app (Aaron version)
* This version is terrible system with just one periodic thread
*/
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include "libchronos/chronos.h"
/* PARAMETERS */
#define SET_SCHED_ALGO SCHED_RT_RMA
//#define SET_SCHED_FLAG (SCHED_FLAG_PI | SCHED_FLAG_HUA | SCHED_FLAG_NO_DEADLOCKS)
#define SET_SCHED_FLAG 0
#define SET_SCHED_PRIO ((SET_SCHED_ALGO & SCHED_GLOBAL_MASK) ? TASK_RUN_PRIO : -1)
#define SET_SCHED_PROC 0
#define UTIL 50
#define PERIOD 1000000
#define EXEC 50
#define JOBS_NUM 32
#define ITER_NUM 4096
/* macros to set or zero cpus in a cpu mask */
#define MASK_ZERO(mask) (mask) = 0
#define MASK_SET(mask, cpu) (mask) |= (unsigned long) pow(2, cpu)
/* TASK_xxx_PRIO */
#define MAIN_PRIO 98
#define TASK_CREATE_PRIO 96
#define TASK_START_PRIO 94
#define TASK_CLEANUP_PRIO 92
#define TASK_RUN_PRIO 90
/* Numerical definitions */
#define THOUSAND 1000
#define MILLION 1000000
#define BILLION 1000000000
static pthread_attr_t attr;
static struct sched_param tparam;
static inline long long timespec_subtract_us(struct timespec *x,
struct timespec *y)
{
long long sec, nsec;
sec = x->tv_sec - y->tv_sec;
nsec = x->tv_nsec - y->tv_nsec;
if (nsec < 0) {
nsec += BILLION;
sec--;
}
return (sec * MILLION) + (nsec / THOUSAND);
}
static void find_job_deadline(int job, long period, struct timespec * start_time, struct timespec *deadline)
{
unsigned long long nsec, carry;
unsigned long offset = (job + 1) * period; //the offset from the start time this deadline is
nsec = start_time->tv_nsec + offset * THOUSAND;
carry = nsec / BILLION;
deadline->tv_nsec = nsec % BILLION;
deadline->tv_sec = start_time->tv_sec + carry;
}
void *start_task(void *arg)
{
int job;
long period = PERIOD;
long i, l;
struct sched_param param;
struct timespec start_time, end_time, deadline, release, tperiod;
tperiod.tv_sec = PERIOD / MILLION;
tperiod.tv_nsec = (PERIOD % MILLION) * THOUSAND;
pid_t tid, pid;
long long tardiness;
unsigned long thread_mask = SET_SCHED_PROC;
//set the affinity of this thread to whatever was specified in the taskset file
if(sched_setaffinity(0, sizeof(thread_mask), (cpu_set_t *) &thread_mask)) {
printf("Failed to set processor affinity of a task.");
pthread_exit(0);
}
//increase priority to TASK_START_PRIO
param.sched_priority = TASK_START_PRIO;
pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
//get the start time and CAS on the start-time so all the threads agree on when they got here
clock_gettime(CLOCK_REALTIME, &start_time);
// periodic LOOP
for (job = 0; job < JOBS_NUM; job++) {
find_job_deadline(job, period, &start_time, &deadline); //find the deadline for this taskset
clock_gettime(CLOCK_REALTIME, &release); // real release time
//enter real-time segment
begin_rtseg_self(TASK_RUN_PRIO, UTIL, &deadline, &tperiod, EXEC);
// DO SOMETHING like a printf... :-)
printf("job %d\n", job);
l = 123456789;
for (i = 0; i < ITER_NUM; i++) {
l *= i;
// or nop
}
i = l;
//end real time segment
clock_gettime(CLOCK_REALTIME, &end_time); //get the endtime
end_rtseg_self(TASK_CLEANUP_PRIO); //end the real-time segment
tardiness = timespec_subtract_us(&deadline, &end_time); //calculate tardiness from deadline and endtime
if (tardiness >= 0) { //otherwise, did we meet our deadline?
//sleep for however much time remains before the next release of this task
if (tardiness > 0)
usleep(tardiness);
}
}
pthread_exit(NULL);
}
int main(int argc, char* argv[]) {
int i;
struct sched_param param, old_param;
unsigned long main_mask = 0;
pthread_t thread;
//TODO we have to put mem lock all and mem unlock all at the end
//set outselves as a real-time task
sched_getparam(0, &old_param);
param.sched_priority = MAIN_PRIO;
if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
printf("sched_setscheduler() failed.");
return 0;
}
//set task affinity of this main thread to the first processor
MASK_ZERO(main_mask);
MASK_SET(main_mask, 0);
if (sched_setaffinity(0, sizeof(main_mask), (cpu_set_t *) & main_mask) < 0) {
printf("sched_setaffinity() failed.");
return 0;
}
if (set_scheduler(SET_SCHED_ALGO | SET_SCHED_FLAG, SET_SCHED_PRIO, SET_SCHED_PROC)) {
printf("Selection of RT scheduler failed! Is the scheduler loaded?");
return 0;
}
tparam.sched_priority = TASK_CREATE_PRIO;
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
pthread_attr_setschedparam(&attr, &tparam);
if (pthread_create(thread, &attr, start_task,0)) {
printf("Failed to pthread_create one of the task threads.");
return 0;
}
if (pthread_join(thread, NULL)) {
printf("Failed to pthread_join one of the task threads.");
return 0;
}
//return us to a normal scheduler and priority
sched_setscheduler(0, SCHED_OTHER, &old_param);
return 0;
}
