Example
From ChronOS Linux
(Difference between revisions)
(Page creation) |
|||
(6 intermediate revisions not shown) | |||
Line 1: | Line 1: | ||
- | This is | + | 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> | ||
+ | /* | ||
+ | * 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; | ||
+ | } | ||
+ | </code> |
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; }