This is an old revision of the document!
The cyclic RT task will use the basic application task which was built in the previous section (HOWTO build a simple RT application).
A cyclic task is one which is repeated after a fixed period of time like reading sensor data every 100 ms. The execution time for the cyclic task should always be less than the period of the task. Following are the mechanisms which we will be looking at for implementing cyclic task:
There are multiple ways to get current time – gettimeofday, time, clock_gettime, and some other processor specific implementations. Some of them, like gettimeofday, will get time from the system clock. The system clock can be modified by other processes. Which means that the clock can go back in time. clock_gettime with CLOCK_MONOTONIC clock can be used to avoid this problem. CLOCK_MONOTONIC argument ensures that we get a nonsettable monotonically increasing clock that measures time from some unspecified point in the past that does not change after system startup[1]. It is also important to ensure we do not waste a lot of CPU cycles to get the current time. CPU specific implementations to get the current time will be helpful here.
Any mechanism for implementing a cyclic task can be divided into the following parts:
The stub for the real time task will look like:
void *simple_cyclic_task(void *data) { struct period_info pinfo; periodic_task_init(&pinfo); while (1) { do_rt_task(); wait_rest_of_period(&pinfo); } }
clock_nanosleep() is used to ask the process to sleep for certain amount of time. nanosleep() can also be used to sleep. But, nanosleep() uses CLOCK_REALTIME which can be changed by another processes and hence can be discontinuous or jump back in time. In clock_nanosleep, CLOCK_MONOTONIC is explicitly specified. This is a immutable clock which does not change after startup. The periodicity is achieved by recording time before and after the task is implemented and sleeping for the difference between period length and execution time (determined by difference in the timestamps taken before RT task has started and after it has completed.) more information on clock_nanosleep at http://man7.org/linux/man-pages/man2/clock_nanosleep.2.html
struct period_info{ struct timespec start_time; struct timespec time_elapsed; }; void periodic_task_init(struct period_info *pinfo) { clock_gettime(CLOCK_MONOTONIC, &(pinfo->start_time)); } void do_rt_work() { //Do RT stuff here. } // Assuming the timespec_a is always greater than timespec_b. struct timespec time_difference(struct timespec *a, struct timespec *b) { struct timespec diff; diff.tv_sec = a->tv_sec - b->tv_sec; diff.tv_nsec = a->tv_nsec - b->tv_nsec; if (diff.tv_nsec < 0) { diff.tv_sec--; diff.tv_nsec += 1000000000; } return diff; } void wait_rest_of_period(struct period_info *pinfo, struct timespec *period_length) { //Determine how much time has passed. //Wait for the rest of the period. struct timespec current_time, time_elapsed, remaining_time; clock_gettime(CLOCK_MONOTONIC, ¤t_time); time_elapsed = time_difference(¤t_time, &pinfo->start_time); // Assuming that the task execution time is less than period_length. remaining_time = time_difference(period_length, &time_elapsed); clock_nanosleep(CLOCK_MONOTONIC, 0, &remaining_time, NULL); // Starting the next period. clock_gettime(CLOCK_MONOTONIC, &pinfo->start_time); }
Recently, earliest deadline first scheduling algorithm has been merged in the mainline kernel. Now, users can specify runtime, period and deadline of a task and they scheduler will run the task every specified period and will make sure the deadline is met. The scheduler will also let user know if the tasks(or a set of tasks) cannot be scheduled because the deadline won't be met.
More information about the EDF scheduler including an example of implementation can be found at: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/scheduler/sched-deadline.txt?id=refs/tags/v4.10-rc2