1 void utime_init(void)
2 {
3 unsigned long long first_time, next_time;
4 unsigned long first_jiffies;
5 int i=0;
6 first_jiffies = jiffies;
7 while (first_jiffies == jiffies);
8 first_time = get_cpuctr();
9 while (i++ < 5) {
10 first_jiffies = jiffies;
11 while (jiffies - first_jiffies < HZ);
12 }
13 next_time = get_cpuctr();
14 cycles_per_sec = (next_time - first_time) / 5;
15 cycles_per_usec = cycles_per_sec / 1000000;
16 cycles_per_jiffies = cycles_per_sec / HZ;
17 base_cpuctr = last_update = get_cpuctr();
18 base_jiffies = jiffies;
19 change_timer_mode(timer_chip_oneshot, 0);
20}
Die neue ISR ruft update_jiffies_u auf um die Systemuhr zu aktualisieren und ermittelt dann die Zeit bis zum nächsten Ereignis. Der Timer- Chip wird dann so programmiert, dass er die CPU zu diesem Zeitpunkt unterbricht. Dadurch wird der Umfang der Berechnungen der ISR wesentlich erhöht. Insbesondere durch die time_to_next_event Funktion, denn während die ISR abgearbeitet wird sind die aktuellen Ereignisse noch am Anfang der Liste. Sie also alle timer übergehen, die bei diesem Interrupt bearbeitet werden und das nächste Ereignis finden, das in der Zukunft liegt.
1 void timer_interrupt()
2 {
3 update_jiffies_u();
4 t = time_to_next_event();
5 program_timer_chip(t);
6 run_timer_list = 1;
7 if (jiffies_intr) {
8 maintain_heart_beat();
9 jiffies_intr--;
10 }
11}
1 void update_jiffies_u(void)
2 {
3 static unsigned long long last_update = 0;
4 unsigned long long this_update;
5 this_update = get_cpuctr();
6 arch_update_jiffies(this_update - last_update);
7 last_update = this_update;
8 }
1 void arch_update_jiffies(unsigned long difference)
2 {
3 unsigned long long current_time;
4 unsigned long new_jiffies, new_jiffies_u;
5 unsigned long seconds, remainder;
6 unsigned long excess_jiffies, excess_cycles, jiffies_jumped;
7 time = get_cpuctr() - base_time;
8 seconds = time / cycles_per_second;
9 remainder = time % cycles_per_second;
10 excess_jiffies = remainder / cycles_per_jiffies;
11 new_jiffies = base_jiffies + (seconds * HZ) + excess_jiffies;
12 excess_cycles = remainder - (excess_jiffies * cycles_per_jiffies);
13 new_jiffies_u = excess_cycles / cycles_per_usec;
14 jiffies_jumped = new_jiffies -jiffies;
15 if (jiffies_jumped) {
16 lost_ticks += jiffies_jumped;
17 jiffies_intr += jiffies_jumped;
18 jiffies = new_jiffies;
19 }
20 jiffies_u = new_jiffies_u;
21}
1 void run_timer_list()
2 {
3 while (timers in queue) {
4 if ((timer->expires < jiffies) || ((timer->expires == jiffies) && (timer->usec <= jiffies_u))) {
5 (*timer->function)(timer->data);
6 timer = timer->next;
7 detach timer from queue;
8 } else {
9 break;
10 }
11 }
12}
int switch_to_rt(int timer_chip_mode, unsigned long period, int rt_mode, char *cmdline, int length);
timer_chip_mode
enthält eine von zwei numerischen Konstanten:period
gibt das Intervall an, mit dem der Kernel unterbrochen werden soll, falls der periodische Modus aktiviert ist.rt_mode
Gibt den Modus, in dem der Echtzeit- Scheduler läuft, an:
cmdline
ist ein Array von Zeichen, das Initialisierungsparameter für die geladenen Echtzeit- Module enthält.length
gibt die Länge des Arrays an.
int switch_to_normal(int force);
force
gibt an, ob sofort in den Normalbetrieb gewechselt werden soll. Wenn dieser Parameter auf 1 gesetzt wird, werden alle eingetragenen Echtzeitereignisse sofort gelöscht und umgeschaltet. Ist er 0 blockiert der Aufruf solange, bis alle verbliebenen Ereignisse abgearbeitet wurden. Bei zyklischem Verhalten, muss force
auf 1 gesetzt werden, da immer wieder neue Ereignisse hinzukommen.
int register_rtmod(struct rtmod_info *info);
1 struct rtmod_info {
2 char *name;
3 unsigned long (*modify_timer_data)(struct rt_event*);
4 int (*init_rtmod)(const char*, const int*);
5 int (*cleanup_rtmod)(void);
6 void (*event_handler)(unsigned long);
7 int (*rtmod_cmd)(int, void *, unsigned long);
8 }
name
ist der Name des Moduls.modify_timer_data
wird von KURT aufgerufen, wenn ein Ereignis für dieses Modul in die Liste mit Eregnissen eingehängt werden soll. Das Modul muss dann den rt_event Struct verarbeiten und einen 4 Byte lange ID zurückliefern, die beim Auslösen des Ereignisses verwendet wird.init_rtmod
wird immer dann ausgeführt, wenn der Kernel in den Echtzeitbetrieb wechselt. Die Initialisierungsparameter des switch_to_rt Aufrufs und derren Anzahl werden übergeben. Hier können modulspezifische Initialisierungen durchgeführt werden.cleanup_rtmod
wird immer aufgerufen, wenn der Kernel vom Echtzeit- wieder in den Normalbetrieb wechselt. Hier können "Aufräumarbeiten" durchgeführt werden.event_handler
wird ausgeführt, wenn ein Ereignis, das von dem Modul unterstützt wird, eintritt. Die 4 Byte lange ID des Ereignisses wird übergeben. Hier wird die Echtzeitaufgabe ausgeführt.rtmod_cmd
erlaubt den Benutzerprozessen über die rtmod_cmd Funktion Befehle an den RTMod zu schicken. Übergeben werden die ID des Befehls, ein Buffer für Daten und die Länge dieses Buffers. Es sollte 0 zurückgeliefert werden, wenn die Abarbeitung erfolgreich war, oder eine negative Zahl, wenn ein Fehler auftrat.
int get_rtmod_num(char *rtmod_name);
int unregister_rtmod(char *name);
int rtmod_cmd(int rtmod_id, int command, void *buffer, int size);
rtmod_id
ist die ID, die das Modul, an das man einen Befehl schicken will, bei der Registrierung erhalten hat. Die ID kann auch über get_rtmod_num festgestellt werden.command, buffer, size
sind die Nummer des Befehls, ein Buffer, der die Daten enthölt, und die Größe dieses Buffers.
int rt_schedule_events(struct timeval *time, int type, int num_times, char *filename);
time
ist der Zeitpunkt, an dem angefangen werden soll, das Ereignis auszulösen. Ist dieser Parameter NULL, so wird damit sofort angefangen. Der Struct timeval ist folgendermaßen definiert:
1 struct timeval {
2 long tv_sec; /* seconds */
3 long tv_usec; /* microseconds */
4 }
type
kann zwei Werte annehmen:
num_times
gibt an, wie oft die geladenen Ereignisse wiederholt werden sollen, falls der Typ auf RT_FROM_MEM gesetzt ist.filename
Name der Schedule- Datei.
1 struct rt_event {
2 unsigned long data_1;
3 unsigned long data_2;
4 unsigned long data_3;
5 unsigned long module_id;
6 unsigned long tenms;
7 unsigned long usec;
8 }
1 int init_module(void) {
2 int status;
3 if ((status = register_rtmod(&parbit_rtmod_info)) < 0){
4 return status;
5 }
6 return 0;
7 }
1 static struct rtmod_info parbit_rtmod_info =
2 {
3 "parbit", /* Module name */
4 NULL, /* Modify timer data */
5 init_parabit, /* Initialization function */
6 NULL, /* Cleanup function */
7 output_val, /* Event handler */
8 NULL, /* rtmod_cmd handler */
9 };
1 int init_parbit(const char *str, const int *ints){
2 if (str)
3 printk(KERN_INFO "Parbit was passed %s as string\n", str);
4 if (ints){
5 printk(KERN_INFO "Parbit was passed %d int args\n", ints[0]);
6 for(int i=1; i<=ints[0]; i++){
7 printk(KERN_INFO "Parbit was passed %d as init arg\n",ints[i]);
8 }
9 }
10 return 0;
11}
1 void output_val(unsigned long useless_data){
2 static int i = 0;
3 if (i==0){
4 outb (0, 0x378); /* turn off parallel port */
5 } else {
6 outb (1, 0x378); /* turn on parallel port */
7 }
8 i = 1 - i;
9 return 0;
10}
1 void cleanup_module(void){
2 unregister_rtmod(parbit_rtmod_info.name);
3 }
1 struct rtparams{
2 int rt_id;
3 int priority;
4 unsigned long proc_req;
5 unsigned long period;
6 }
rt_id
wird benutzt um in der Schedule- Datei den Prozess zu referenzieren. Wird ASSIGN_RT_ID angegeben, weist das Process RTMod dem Prozess eine ID zu.priority
wird in dem Round- Robin- Scheduler, der SCHED_KURT- Prozesse verarbeitet, die keine expliziten Ausführungszeitpunkte haben, verwendet.proc_req
ist die CPU- Zeit in Microsekunden, die der (periodische) Prozess jedesmal benötigt wenn er aufgerufen wird. Für Prozesse mit expliziten Ausführungszeiten muss hier eine 0 angegeben werden.period
ist das Intervall in Microsekunden in dem ein periodischer Echtzeit- Prozess aufgerufen wird. Echtzeit- Prozesse, derren Ereignisse von seperaten Prozessen ausgelöst werden, müssen hier eine 0 eintragen.
int set_rtparams(int pid, int policy, struct rtparams *param);
pid
ist die Prozess- ID des Prozesses, für den der Aufruf ausgeführt werden soll. Wird 0 angegeben wird die PID des aufrufenden Prozesses genommen.policy
wird SCHED_KURT angegeben, wird der Prozess als Echtzeit- Prozess markiert. Wird SCHED_OTHER angegeben wird er als normaler Prozess behandelt.param
enthält die Charakteristika eines Echtzeit- Prozesses. (Wie in der rtparams Struktur angegeben) Kann NULL sein, wenn policy
SCHED_OTHER ist.
int get_rtparams(int pid, struct rtparams *params);
int get_rtstats(int pid, struct rtstats *info);
1 struct rtstats{
2 int rt_abort;
3 int suspended;
4 unsigned long rt_num_woken_up;
5 unsigned long rt_num_suspended;
6 unsigned long rt_num_missed;
7 }
rt_abort
ist ein boolsches Flag, das gesetzt wird, wenn der Prozess die Zeit, die ihm zugestanden wurde, überschritten hat und vom nachfolgenden Prozess verdrängt wurde. Merkt ein Prozess, dass rt_abort
gesetzt ist, sollte er abbrechen, was er gerade tut und rt_suspend aufrufen.suspended
ist ein boolsches Flag, das gesetzt ist, wenn der Prozess rt_suspend aufgerufen hat.rt_num_woken_up, rt_num_suspended
gibt an, wie oft der Prozess aufgeweckt bzw. ausgesetzt wurde.rt_num_missed
gibt an, wie oft versucht wurde, den Prozess aufzuwecken, ohne dass er zuvor rt_suspend aufgerufen hat.
int get_num_rtprocs(void);
int rt_suspend(int susp_mode);
susp_mode
kann nur bestimmte Werte annehmen:
1 void main(){
2 set_rtparams(); /* inform KURT about processing requirements etc. */
3 switch_to_rt(); /* switch kernel to real-time mode */
4 for (i=0;i<num_iters;i++){
5 rt_suspend(); /* suspend until next period */
6 do_stuff(); /* process information for this period */
7 }
8 switch_to_normal();
9 }
1 int main(int argc, char **argv){
2 struct rtparams rt_params;
3 struct timeval tv[5000];
4 int i;
5 rt_params.rt_id = 1;
6 rt_params.proc_req = 500;
7 rt_params.period = 10000;
8 rt_params.priority = (MAX_KURT_PRIORITY + MIN_KURT_PRIORITY)/2;
9 if (set_rtparams(0, SCHED_KURT, &rt_params)){
10 perror("set_rtparams");
11 exit(1);
12 }
13 get_rtparams(0, &rt_params);
14 if (switch_to_rt(timer_chip_oneshot, 0, SCHED_ALL_PROCS, NULL, 0)){
15 perror("switch_to_rt");
16 exit(1);
17 }
18 for (i = 0; i < 5000; i++){
19 if (rt_suspend(SUSPEND_IF_NRT | START_SCHED)){
20 perror("rt_suspend");
21 exit(0);
22 }
23 gettimeofday (&tv[i], NULL);
24 }
25 rt_suspend(STOP_SCHED);
26 switch_to_normal(1);
27}
1 int main(int argc, char **argv){
2 struct rtparams rt_params;
3 struct timeval tv[5000];
4 int i = 0;
5 rt_params.rt_id = 1;
6 rt_params.proc_req = 0;
7 rt_params.period = 0;
8 rt_params.priority = MAX_KURT_PRIORITY;
9 if (set_rtparams(0, SCHED_KURT, &rt_params)){
10 perror("set_rtparams");
11 exit(1);
12 }
13 while (1){
14 if (rt_suspend(SUSPEND_IF_NRT | START_SCHED)){
15 perror("rt_suspend");
16 exit(0);
17 }
18 if (i >= 5000){
19 i = 0;
20 }
21 gettimeofday (&tv[i++], NULL);
22 }
23}
1 int main(){
2 set the scheduler of the current process to SCHED_KURT;
3 wait for all of the KURT processes to register themselfs;
4 switch to real-time mode;
5 schedule the real-time events;
6 switch to normal mode;
7 set the scheduler of the current process to SCHED_OTHER;
8 }
1 int main(int argc, char **argv){
2 struct rtparams rt_params;
3 rt_params.rt_id = 0;
4 rt_params.rt_proc_req = 0;
5 rt_params.period = 0;
6 rt_params.priority = (MAX_KURT_PRIORITY + MIN_KURT_PRIORITY)/2;
7 if (set_rtparams(0, SCHED_KURT, &rt_params)){
8 perror("set_rtparams");
9 exit(1);
10 }
11 get_rtparams(0, &rt_params);
12 while (get_num_rtprocs() < 2){
13 sleep(1);
14 }
15 if (switch_to_rt(time_chip_oneshot, 0, SCHED_ALL_PROCS, NULL, 0)){
16 perror("switch_to_rt");
17 exit(1);
18 }
19 if (rt_schedule_events(NULL, RT_FROM_MEMORY, 3, "schedule_file")){
20 perror("rt_schedule_events");
21 exit(1);
22 }
23 sleep(1);
24 if (switch_to_normal(0)){
25 perror("switch_to_normal failed");
26 }
27 rt_params.rt_id=0;
28 if (set_rtparams(0, SCHED_OTHER, &rt_params)){
29 perror("set_rtparams failed");
30 }
31}
Number of Processes | % of events found within 'x' Microseconds of Expected Time | ||||
10 µsec | 50 µsec | 100 µsec | 500 µsec | 1000 µsec | |
1 | 62.22% | 99.59% | 99.80% | 100.00% | 100.00% |
10 | 3.36% | 15.12% | 30.25% | 99.34% | 99.72% |
20 | 1.44% | 7.45% | 14.76% | 66.48% | 99.78% |
30 | 1.21% | 6.10% | 11.03% | 46.61% | 73.11% |
Number of Processes | % of events found within 'x' Microseconds of Expected Time | ||||
10 µsec | 50 µsec | 100 µsec | 500 µsec | 1000 µsec | |
1 | 99.80% | 100.00% | 100.00% | 100.00% | 100.00% |
10 | 96.66% | 99.47% | 99.67% | 100.00% | 100.00% |
20 | 99.34% | 99.67% | 99.91% | 100.00% | 100.00% |
30 | 97.33% | 99.54% | 99.80% | 99.99% | 100.00% |
Number of Processes | % of events found within 'x' Microseconds of Expected Time | ||||
10 µsec | 50 µsec | 100 µsec | 500 µsec | 1000 µsec | |
1 | 99.80% | 99.80% | 100.00% | 100.00% | 100.00% |
10 | 96.61% | 99.72% | 99.87% | 100.00% | 100.00% |
20 | 99.23% | 99.40% | 99.61% | 99.80% | 100.00% |
30 | 99.52% | 99.75% | 99.89% | 100.00% | 100.00% |
Number of Processes | % of events found within 'x' Microseconds of Expected Time | ||||
10 µsec | 50 µsec | 100 µsec | 500 µsec | 1000 µsec | |
1 | 50.20% | 81.33% | 83.74% | 98.99% | 99.59% |
10 | 5.50% | 17.19% | 30.70% | 84.40% | 92.25% |
20 | 2.00% | 7.07% | 13.21% | 55.50% | 84.20% |
30 | 1.09% | 4.71% | 8.65% | 36.47% | 64.32% |
Number of Processes | % of events found within 'x' Microseconds of Expected Time | ||||
10 µsec | 50 µsec | 100 µsec | 500 µsec | 1000 µsec | |
1 | 40.16% | 60.04% | 73.89% | 97.60% | 99.60% |
10 | 68.43% | 81.68% | 88.61% | 97.17% | 99.17% |
20 | 87.94% | 95.17% | 97.37% | 99.52% | 99.66% |
30 | 56.26% | 60.74% | 65.27% | 82.01% | 95.72% |
... [ Linux als Echtzeitbetriebssystem ]
... [ Thema Echtzeit für Linux ]
... [ KURT als eine mögliche Lösung ]
... [ Referenzen ] ...