Abwärtskompatibilität

Task-Schlangen und das Timing sind über die Jahre relativ konstant geblieben, aber einige Dinge haben sich trotzdem geändert und müssen beachtet werden.

Die Funktionen sleep_on_timeout, interruptible_sleep_on_timeout und schedule_timeout wurden alle für den 2.2-Kernel hinzugefügt. Zu Zeiten von 2.0 wurden Timeouts mit einer Variable namens timeout in der Task-Struktur abgedeckt. Code, der heute so aussieht:


 interruptible_sleep_on_timeout(my_queue, timeout);

sah damals also so aus:


current->timeout = jiffies + timeout;
interruptible_sleep_on(my_queue);

In der Header-Datei sysdep.h wird schedule_timeout für Kernel vor 2.4 nachimplementiert, so daß Sie die neue Syntax auch unter 2.0 und 2.2 verwenden können:


extern inline void schedule_timeout(int timeout)
{
    current->timeout = jiffies + timeout;
    current->state = TASK_INTERRUPTIBLE;
    schedule();
    current->timeout = 0;
}

In 2.0 gab es eine Reihe zusätzlicher Funktionen, um Funktionen in Task-Schlangen einzustellen. queue_task_irq konnte in Situationen, in denen Interrupts abgeschaltet waren, anstelle von queue_task aufgerufen werden, was eine (sehr) kleine Performance-Verbesserung mit sich brachte. queue_task_irq_off ist noch schneller, funktioniert aber nicht richtig, wenn der Task schon in die Schlange gestellt ist oder schon läuft, und kann daher nur verwendet werden, wenn das garantiert nicht der Fall sein kann. Keine dieser beiden Funktionen hat aber besonders viel Performance-Gewinn gebracht, weswegen sie beide im Kernel 2.1.30 entfernt wurden. Verwenden Sie immer queue_task; sie funktioniert mit allen Kernel-Versionen. (Beachten Sie aber, daß queue_task in 2.2 und davor den Rückgabetyp void hatte.)

Vor 2.4 gab es weder die Funktion schedule_task noch den dazugehörigen Prozeß keventd. Statt dessen gab es eine andere vordefinierte Task-Schlange namens tq_scheduler. Tasks in tq_scheduler wurden in der schedule-Funktion ausgeführt und liefen deswegen immer im Prozeß-Kontext. Wessen Kontext verwendet wurde, war aber immer verschieden; es war immer derjenige Prozeß, der gerade an der CPU anstand. tq_scheduler hatte meistens größere Latenzen — insbesondere bei Tasks, die sich selbst wieder eingestellt hatten. sysdep.h enthält die folgende Implementation von schedule_task für 2.0- und 2.2-Systeme:


extern inline int schedule_task(struct tq_struct *task)
{
        queue_task(task, &tq_scheduler);
        return 1;
}

Wie bereits erwähnt, wurde in der 2.3-Entwicklungsserie der Tasklet-Mechanismus eingeführt; vorher standen Task-Schlangen nur für die “unmittelbare verzögerte” Ausführung zur Verfügung. Das Subsystem der unteren Hälften war anders implementiert, obwohl die meisten Änderungen für Gerätetreiber-Entwickler gar nicht sichtbar sind. Wir haben keine Tasklets für ältere Kernel in sysdep.h emuliert, weil diese nicht unbedingt notwendig sind, um einen Treiber zu schreiben. Wenn Sie abwärtskompatibel sein wollen, dann müssen Sie entweder eine eigene Emulation schreiben oder statt dessen Task-Schlangen verwenden.

Die Funktion in_interrupt existierte in Linux 2.0 noch nicht. Statt dessen gab es eine globale Variable namens intr_count, in der die Anzahl der gerade laufenden Interrupt-Handler gespeichert war. Das Auslesen von intr_count ist semantisch das gleiche wie das Aufrufen von in_interrupt, so daß es kein Problem war, die Kompatibilität in sysdep.h zu gewährleisten.

Die Funktion del_timer_sync existiert erst seit dem Entwicklungs-Kernel 2.4.0-test2. sysdep.h definiert einen minimalen Ersatz, wenn Sie mit älteren Kernel-Header-Dateien kompilieren. In der Kernel-Version 2.0 gab es auch kein mod_timer. Auch diese Lücke wird von unserer Header-Datei gefüllt.