Open Source im professionellen Einsatz

Pünktlich und priorisiert

Die Anwendung der Echtzeit-Mutexe ist einfach: Im Kernel gibt es die Datenstruktur »struct rt_mutex« und die darauf aufbauenden Funktionen »rt_mutex_lock()«, »rt_mutex_lock_interruptible()«, »rt_mutex_timed_lock()« und »rt_mutex_unlock()«. Wie in Listing 1 zu sehen, ruft der Entwickler die Funktion »rt_mutex_lock()« (im Code in der Variante »rt_mutex_lock_interruptible()«) auf, bevor sein Code den kritischen Abschnitt betritt. Gleiches gilt für »rt_mutex_unlock()«, um das Ende des kritischen Abschnitts zu signalisieren.

Die Funktionen »rt_mutex_lock()« und »rt_mutex_lock_interruptible()« unterscheiden sich nur dadurch, dass sich letztere durch ein Signal unterbrechen lässt, erstere nicht. Die Funktion »rt_mutex_timed_lock()« sorgt dafür, dass ihr Thread nicht unendlich lange auf die Freigabe des kritischen Abschnitts wartet.

Listing 1: Zwei Threads in
»prioinv.c« vererben ihre Priorität

01 #include <linux/module.h>
02 #include <linux/version.h>
03 #include <linux/init.h>
04 #include <linux/sched.h>
05 #include <linux/completion.h>
06 #include <linux/kthread.h>
07 
08 static wait_queue_head_t wq;
09 static DECLARE_COMPLETION(on_exit);
10 static struct rt_mutex tmut;
11 static struct task_struct *plow, *phigh;
12 
13 static int process(void *data) {
15   unsigned long to, i;
16   struct sched_param schedpar;
18 
19   schedpar.sched_priority = (long)data;
20   sched_setscheduler(current, SCHED_FIFO, &schedpar);
22   printk("Thread %d Prio %d: rt_mutex_lock().n",
24          current->pid, current->rt_priority);
26 
27   // Begin des kritischen Abschnitts
28   if (rt_mutex_lock_interruptible(&tmut, 0) < 0) {
30     printk("Interrupt waehrend rt_mutex_lock_interruptiblen");
31   } else {
32     printk("Thread %d, Prio %d: im kritischen Abschnitt.n",
34            current->pid, current->rt_priority);
35     for (i = 0; i < 5; i++) {
36       to = HZ; // eine Sekunde warten
37       to = wait_event_interruptible_timeout(wq, (to == 0), to);
39       printk("Thread %d Prio %d: aktuelle Prioritaet: %dn",
40              current->pid, current->rt_priority, 99 - current->prio);
43     }
44   }
45   printk("Thread %d Prio %d: rt_mutex_unlock().n",
47          current->pid, current->rt_priority);
49   rt_mutex_unlock(&tmut);
50   // Ende des kritischen Abschnitts
51 
52   printk("Thread %d Prio %d: aktuelle Prioritaet: %dn",
54          current->pid, current->rt_priority, 99 - current->prio);
56   complete_and_exit(&on_exit, 0);
57 }
58 
59 static int __init kthread_init(void) {
61   unsigned long to; // Timeout
62 
63   init_waitqueue_head(&wq);
64   rt_mutex_init(&tmut);
65 
66   printk("Thread mit niedriger Prio...n");
67   plow = kthread_create(process, (void *)50, "prio-low");
69   if (IS_ERR(plow)) return -EFAULT;
70   wake_up_process(plow);
71 
72   to = HZ; // eine Sekunde warten
73   to = wait_event_interruptible_timeout(wq, (to == 0), to);
74 
75   printk("Thread mit hoher Prio ...n");
76   phigh = kthread_create(process, (void *)70, "prio-high");
78   if (IS_ERR(phigh)) return -EFAULT;
79   wake_up_process(phigh);
81   return 0;
82 }
83 
84 static void __exit kthread_exit(void) {
86   printk("kthread_exit()n");
87   wait_for_completion(&on_exit); wait_for_completion(&on_exit);
89 }
90 
91 module_init(kthread_init); module_exit(kthread_exit);
94 MODULE_LICENSE("GPL");

Prozess rechts überholt

Wer Listing 1 kompiliert, erhält Einblick in die Prioritätsvererbung. Lädt er das Modul, erzeugt es zwei Threads. Einer erhält eine niedrige Priorität von 50, der andere eine hohe von 70. Das Beispiel räumt dem Thread mit niedriger Priorität genug Zeit ein, um den über das Realtime-Mutex geschützten kritischen Abschnitt zu betreten. Danach aktiviert es den zweiten Thread, der ebenfalls »rt_mutex_lock()« aufruft. Der bevorzugte Prozess wartet auf das RT-Mutex des niedriger eingestuften. Daher vererbt der Kernel die hohe Priorität auf den nachrangigen Prozess (Abbildung 3).

Abbildung 3: Der hochpriorisierte Thread 19679 vererbt dem niedrig priorisierten Thread 19677 seine Vorrangstellung, weil dieser ein gemeinsam genutztes Lock hält. Nun geht es für beide schneller voran.

Abbildung 3: Der hochpriorisierte Thread 19679 vererbt dem niedrig priorisierten Thread 19677 seine Vorrangstellung, weil dieser ein gemeinsam genutztes Lock hält. Nun geht es für beide schneller voran.

Die aktuelle Priorität des Thread speichert Linux in dem Element »prio« des Prozess-Kontrollblocks »struct task_struct«. Da für den Kernel - entgegengesetzt der Definition am Programmier-Interface - jedoch 0 eine hohe und 99 eine niedrige Echtzeitpriorität bedeutet, rechnen die Zeilen 35 und 43 die Werte wieder in Posix-Darstellung zurück.

Die Kernelentwickler haben Prioritätsvererbung durchaus kontrovers diskutiert: So schrieb Linus Torvalds noch im Dezember 2005: "Friends don\'t let friends use priority inheritance." Dass er die Prioritätsvererbung letztlich doch in den Kernel eingebaut hat, zeigt, dass er durchaus in der Lage ist, seine Meinung zu revidieren. (mg)

Infos

[1] Eva-Katharina Kunst, Jürgen Quade, "Linux-Treiber entwickeln": Dpunkt-Verlag, 2. Auflage, Juni 2006

[2] Willi Nüßer, "Kampf um die Ressourcen": Linux-Magazin 10/05, S. 84; [http://www.linux-magazin.de/heft_abo/ausgaben/2005/10/kampf_um_die_ressourcen]

[3] Ulrich Drepper, "Tricky Futexes": [http://people.redhat.com/drepper/futex.pdf]

[4] E.-K. Kunst, J. Quade: "Realtime-Preemption", Linux-Magazin 07/07, S. 102

[5] Ingo Molnar, "Robust Futexes", Kerneldokumentation: [http://lxr.linux.no/linux/Documentation/robust-futexes.txt]

Die Autoren

Eva-Katharina Kunst, Journalistin, und Jürgen Quade, Professor an der Hochschule Niederrhein, sind seit den Anfängen von Linux Fans von Open Source. Unter dem Titel "Linux Treiber entwickeln" haben sie zusammen ein Buch zum Kernel 2.6 veröffentlicht.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 4 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook