Chapter 4 RTLinux Application Design
4.1 program structure
Each real-time application can be divided into two parts: real-time parts and non-real time [2]. Non-real-time sections are executed in user space, called user part. The real-time part should be as simple as possible, only the code directly related to time; due to the constraint of the hardware, the code of the low-level and hardware communication is generally included in the real-time portion. The code of the user part is mainly implemented as data processing, including the release, saving, and user interface of the data. Communication between two parts uses a data buffer.
Data flowchart shown in Figure 4.1 is a typical real-time application in accordance with this program model.
Figure 4.1 Program structure
4.2 Basic API
4.2.1 POSIX Thread Creation Function
As mentioned earlier, a real-time program consists of several threads executed. The thread is a lightweight process that shares a common address space. In RTLINUX, all threads share the Linux kernel address space.
INT pthread_create (pthread_t * thread, pthread_attr_t * attr, void * (* st_routine) (void *), void * arg)
This is the standard POSIX thread creation function of RTLinux. This thread runs the function pointer start_routine to the process, and the arg is the entry parameter of the pointer of this function. The attribute of the thread is determined by the Attr object, and attributes can be set for this attribute, stack size. Setting if null will use the default properties. Returns 0 Successfully creates a thread, the thread number is placed in the space pointed to by Thread; returning non-0 means creation failure. The thread's attribute determines whether to create a thread (pthread_attr_setcpu_np) on a particular CPU, use the FPU (pthread_attr_setfp_np).
INT PTHREAD_ATTR_INIT (PTHREAD_ATTR_T * ATTR)
Initialize the properties of thread running.
int pthread_ attr_setschedparam (pthread_attr_t * attr, const struct sched_param * param) and int pthread_ attr_setschedparam (const pthread_attr_t * attr, struct sched_param * param)
These two functions are set according to the needs of the program correspondingly from the ATTR to run the running parameters of the thread. Param is the property defined for scheduled SCHED_FIFO and SCHED_RR policy.
INT PTHREAD_ATTR_SETCPU_NP (pthread_atte_t * attr, int CPU) and
INT PTHREAD_ATTR_GETCPU_NP (PTHREAD_ATTE_T * ATTR, INT CPU)
Set / get the CPU number of thread running. Allows the thread to run on a specific CPU on a specific CPU on a specific CPU.
INT PTHREAD_CANCEL (PTHREAD_T THREAD)
Cancel a running thread.
INT pthread_delete_np (pthread_t thread)
Delete a thread and release all resources of the thread. Returning 0 means successful deletion, non-0 means deleting failed.
Pthrad_t pthread_self (void)
Get the current running thread number.
ClockID_T RTL_GETSCHEDCLOCK (VOID)
Get the clock of the current scheduling method.
INT RTL_SETCLOCKMODE (Clockid_t Clock, INT MODE, HRTIME_T MODE_PARAM)
Set the current clock mode, mode = RTL_CLOCK_MODE_ONESHOT is a non-cycle (one-time) mode mode_param parameter is useless; mode = RTL_CLOCK_MODE_PERIODIC is cycle mode, and the mode_param parameter is the length of the cycle. (Instructions for see Section 3.4 in Clock Mode) INT PTHREAD_WAIT_NP (VOID)
The current cycle thread runs, always returns 0.
4.2.2 Time-related functions
RTLinux provides some clock functions for timing functions, including thread scheduling, and obtain TSP (TimeStamps).
The following is a general timing function:
/ * #Include
* /
INT Clock_gettime (clockid_t clock_id, struct timespec * ts);
Hrtime_t clock_gethrtime (clockid_t clock);
Struct timeespec {
Time_t tv_sec; / * second * /
Long TV_nsec; / * nanosene * /
}
Clock_gettime: Read the current time and saved to the object referred to in CLOCK_ID.
Clock_gethrtime: Reads the current time, but returns a 64-bit (HRTIME_T) nanosecond time value.
Some time conversion functions are used to convert the time format to another format.
Time conversion function:
/ * #Include
* /
HRTIME_T TIMESPEC_TO_NS (const struct timespec * ts); / * Timespec to Nancix conversion * /
Struct Timespec Timespec_FROM_NS (HRTIME_T T); / * Nancatch to Timespec Conversion * /
Const struct timeespec * hrt2ts (hrtime_t value); / *
Here are some supported clock types.
Clock type related macro:
l Clock_monotonic: POSIX clock, running constant rate; no reset and adjustment
l Clock_RealTime: Standard POSIX real-time clock. Currently the same as the Clock_Monotonic clock
l clock_rtl_sched: the clock used to schedule to schedule
The following is the machine structure related clock:
l Clock_8254: Clock for scheduling on the x86 single processor machine
l Clock_APIC: Use the clock in the SMP X86 machine
4.2.3 Thread Scheduling Function
RTLINUX provides some scheduling, allowing thread code to run at a specific time. RTLINUX uses a simple priority-driven scheduler, and more priority threads are always selected. If the priority of the two threads has the same priority, select that thread run is uncertain. RTLINUX uses the following scheduling API:
INT PTHREAD_SETSCHEDPARAM (PThread_t Thread, Int Policy, Const struct sched_param * param)
Set a scheduling parameter of a thread, set two scheduling parameter properties of Thread with both policy and sched_param:
Policy = SCHED_RR: Using Round-Robin Method Scheduling
Policy = SCHED_FIFO: Use advanced first out method scheduling
Returning 0 indicates success schedule, non-0 means failure.
INT PTHREAD_GETSCHEDPARAM (pthread_t thread, int policy, const struct sched_param * param) Gets a scheduling parameter for a thread. Place the obtained Policy and SCHED_PARAM structure in the address pointed to by the inlet parameter.
INT PTHREAD_MAKE_PERIDIC_NP (pthread_t thread, hrtime start_time, hrtime_t period)
This function mark Thread thread is run. The thread will start running at the START_TIME, and the time interval run is given by Period.
INT pthread_wait_np (void)
The pthread_wait_np function will hang the current running process until the next cycle. This thread must be a pthread_make_periodic_np function tag as executable.
INT SCHED_GET_PRIORITY_MAX (INT Policy) and
INT SCHED_GET_PRIORITY_MIN (INT Policy)
Determine the possible value of Sched_Priority.
4.3 Programming Example
The basic API of RTLinux is described above, where an example is described herein to illustrate the programming method under RTLinux. This is a program that interrupts the delay under RTLinux. As mentioned earlier, the program is divided into two parts, real-time parts and non-real time. The real-time part runs the real-time task after inserting the real-time module inserting the real-time module. For non-real-time parts, communication to the FIFO device is implemented, completed, and real-time tasks.
Figure 4.2 Real-time program structure
4.3.1 Real-Time Part
INIT_MODULE completes the initialization of the real-time part. Cleanup_module implements tasks that turn off real-time modules.
/ * * RTLINUX SCHEDULING ACCURACY Measuring Example * /
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "common.h"
INT NTESTS = 500;
INT Period = 1000000;
Int bperiod = 3100000;
INT MODE = 0;
INT ABSOLUTE = 0;
INT FIFO_SIZE = 4000;
INT Advance = 0;
Module_Parm (Period, "I");
Module_Parm (Bperiod, "I");
Module_Parm (NTESTS, "I");
Module_Parm (Mode, "I");
Module_Parm (Absolute, "I");
Module_Parm (Advance, I ");
Pthread_t thread;
INT fd_fifo;
Void * thread_code (void * param)
{
HRTIME_T EXPECTED;
HRTIME_T DIFF;
HRTIME_T NOW;
HRTIME_T LAST_TIME = 0;
HRTIME_T min_diff;
HRTIME_T MAX_DIFF;
Struct Sample SAMP;
INT I;
INT CNT = 0;
INT CPU_ID = RTL_GETCPUID ();
RTL_PrINTF ("Measurement Task Starts On CPU% D / N", CPU_ID);
IF (Mode) {
int RET = RTL_SETCLOCKMODE (Clock_RealTime, RTL_Clock_Mode_Periodic, Period); if (Ret! = 0) {
Conpr ("Setting Periodic Mode Failed / N);
Mode = 0;
}
} else {
RTL_SETCLOCKMODE (Clock_RealTime, RTL_Clock_Mode_ONESHOT, 0);
}
Expected = clock_gethrtime (clock_realtime) 2 * (HRTIME_T) period;
FD_FIFO = Open ("/ dev / RTF0", O_NONBLOCK);
IF (FD_FIFO <0) {
RTL_PRINTF ("/ dev / rtf0 open return% D / N", fd_fifo);
Return (void *) -1;
}
IF (Advance) {
RTL_Stop_Interrupts (); / * be Careful with this! The task won't be preempted by anything else. this is probably only appropriate for small high-priority tasks. * /
}
/ * first cycle * /
Clock_nanosleep (Clock_RealTime, Timer_Abstime, HRT2TS (Expected - Advance), NULL)
EXPECTED = period;
Now = clock_gethrtime (clock_monotonic);
Last_time = now;
Do {
Min_diff = 2000000000;
Max_diff = -2000000000;
For (i = 0; i CNT; Clock_nanosleep (Clock_RealTime, Timer_Abstime, HRT2TS (Expected - Advance), NULL) Now = clock_gethrtime (clock_monotonic); IF (Absolute && Advance &&! Mode) { IF (now RTL_DELAY (Expected - Now); } Now = clock_gethrtime (clock_monotonic); } IF (absolute) { DIFF = NOW - EXPECTED; } else { DIFF = NOW - Last_Time - PERIOD; IF (Diff <0) { DIFF = -diff; } } IF (DIFF Min_diff = DIFF; } IF (DIFF> MAX_DIFF) { MAX_DIFF = DIFF; } EXPECTED = period; Last_time = now; } SAMP.MIN = min_diff; SAMP.MAX = Max_diff; Write (FD_FIFO, & Samp, Sizeof (Samp)); WHILE (1); Return 0; } Pthread_t Background_threadID; Void * background_thread (void * param) { Hrtime_t next = clock_gethrtime (Clock_RealTime); While (1) { HRTIME_T T = gethrtime (); NEXT = BPERIOD; / * The measurement task limited preempt the folload loop * / While (gethrtime () Clock_nanosleep (Clock_RealTime, Timer_Abstime, HRT2TS (Next), NULL; } } INT init_module (void) { PTHREAD_ATTR_T ATTR; Struct sched_param sched_param; INT THREAD_STATUS; INT FIFO_STATUS; RTF_DESTROY (0); FIFO_STATUS = RTF_CREATE (0, FIFO_SIZE); IF (FIFO_STATUS) { RTL_Printf ("RTLinux Measurement Test Fail. Fifo_status =% D / N", FIFO_STATUS; Return -1; } RTL_Printf ("RTLinux Measurement Module On CPU% D / N", RTL_Getcpuid ()); Pthread_attr_init (& Attr); IF (RTL_CPU_EXISTS (1)) { PTHREAD_ATTR_SETCPU_NP (& ATTR, 1); } Sched_Param.Sched_Priority = 1; Pthread_attr_setschedparam (& Attr, & Sched_PARAM); RTL_Printf ("About to Thread Create / N); Thread_status = pthread_create (& Thread, & attr, thread_code, (void *) 1); IF (thread_status! = 0) { RTL_Printf ("Failed to Create RT-Thread:% D / N", Thread_status); Return -1; } else { RTL_Printf ("CREATED RT-Thread / N"); } IF (bperiod) { Pthread_create (& Background_threadID, NULL, Background_thread, NULL); } Return 0; } Void Cleanup_Module (Void) { RTL_PrINTF ("Removing Module On CPU% D / N", RTL_Getcpuid ()); Pthread_cancel (thread); pthread_join (thread, null); Close (FD_FIFO); RTF_DESTROY (0); IF (bperiod) { pthread_cancel (Background_threadID); pthread_join (Background_threadid, NULL); } } 4.3.2 Non-real time Non-real-time part actually CCB is an application that can be written under the console. However, you must have / dev / RTF0 device read permissions to run. #include #include #include # include # include #include #include #include #include "common.h" int main () { INT FD0; Int n; Struct Sample SAMP; IF ((FD0 = Open ("/ dev / RTF0", O_RDONLY)) <0) { FPRINTF (stderr, "error opening / dev / RTF0 / N"); Exit (1); } While (1) { n = read (FD0, & Samp, Sizeof (SAMP)); Printf ("Min:% 8D, MAX:% 8D / N", (int) samp.min, (int) samp.max); Fflush (stdout); } Return 0; } 4.3.3 Compilation and Operation Procedures In a Celeron 412MHz, 196MB memory, the following Makefile compiles on the machine of RTLINUX3.1: All: RT_Process.o Irqsema.o Monitor Histplot INCLUDE ../../rtl.mk Monitor: Monitor.c $ (Cc) $ {user_cflags} $ {include} -wall -o2 -o monitor monitor.c Clean: RM -F * .o Monitor Histplot Periodic_Monitor Gnuplot.out INCLUDE $ (RTL_DIR) /Rules.make Then the program can test the time accuracy of the schedule, and the result of the program is: MIN: 0, MAX: 24480 MIN: 0, MAX: 10720 MIN: 0, MAX: 10912 MIN: 0, MAX: 10976 MIN: 0, MAX: 11072 MIN: 0, MAX: 10656 MIN: 0, MAX: 10944 MIN: 0, MAX: 11200 MIN: 0, MAX: 11200 MIN: 0, MAX: 11008 MIN: 0, MAX: 10912 .........