Heim  >  Artikel  >  System-Tutorial  >  Detaillierte Erläuterung der Verwendung von Linux-Kernel-Timern

Detaillierte Erläuterung der Verwendung von Linux-Kernel-Timern

WBOY
WBOYnach vorne
2024-02-09 13:50:27996Durchsuche

Der Linux-Kernel-Timer ist ein Mechanismus, der vom Kernel verwendet wird, um die Planung und Ausführung einer bestimmten Funktion zu einem bestimmten Zeitpunkt in der Zukunft (basierend auf Sekunden) zu steuern. Die Implementierung dieses Mechanismus befindet sich in den Dateien und kernel/timer.c.

Detaillierte Erläuterung der Verwendung von Linux-Kernel-Timern

Die geplante Funktion muss asynchron ausgeführt werden. Sie ähnelt einem „Software-Interrupt“ und befindet sich in einem Nicht-Prozess-Kontext. Daher muss die Planungsfunktion die folgenden Regeln befolgen:

  1. Ohne den aktuellen Zeiger ist kein Zugriff auf den Benutzerbereich zulässig. Da kein Prozesskontext vorliegt, hat der entsprechende Code keinen Bezug zum unterbrochenen Prozess.
  2. Schlaf oder Funktionen und Zeitpläne, die Schlaf verursachen können, können nicht ausgeführt werden.
  3. Jede Datenstruktur, auf die zugegriffen wird, sollte vor gleichzeitigem Zugriff geschützt werden, um Race Conditions zu verhindern.

Sobald die Kernel-Timer-Planungsfunktion einmal ausgeführt wurde, wird sie nicht erneut ausgeführt (entspricht der automatischen Abmeldung). Es ist jedoch möglich, die Ausführung periodisch durchzuführen, indem sie sich innerhalb der geplanten Funktion selbst neu plant.

In einem SMP-System läuft die Planungsfunktion immer auf derselben CPU, auf der sie registriert wurde, um so viel Cache-Lokalität wie möglich zu erhalten.

Timer-API

Datenstruktur des Kernel-Timers

struct timer_list {
  struct list_head entry;

  unsigned long expires;
  void (*function)(unsigned long);
  unsigned long data;

  struct tvec_base *base;
  /* ... */
};

Das Ablauffeld stellt den Jiffies-Wert dar, den der Timer voraussichtlich ausführen wird. Wenn der Jiffies-Wert erreicht ist, wird die Funktion aufgerufen und Daten werden als Parameter übergeben. Wenn ein Timer beim Kernel registriert ist, wird das Eingabefeld verwendet, um den Timer mit einer verknüpften Kernelliste zu verbinden. Das Basisfeld wird intern von der Kernel-Implementierung verwendet.
Beachten Sie, dass der Ablaufwert 32 Bit beträgt, da Kernel-Timer nicht für lange zukünftige Zeitpunkte gelten.

Initialisierung

Bevor Sie struct timer_list verwenden, müssen Sie die Datenstruktur initialisieren, um sicherzustellen, dass alle Felder korrekt eingestellt sind. Es gibt zwei Methoden zur Initialisierung.
Methode 1:
DEFINE_TIMER(Timer_Name, Funktionsname, Ablaufwert, Daten);
Dieses Makro erstellt statisch einen Kernel-Timer mit dem Namen timer_name und initialisiert dessen Funktion, Ablauf, Namen und Basisfelder.

Methode 2:

struct timer_list mytimer;
setup_timer(&mytimer, (*function)(unsigned long), unsigned long data);
mytimer.expires = jiffies + 5*HZ;
方法3:
struct timer_list mytimer;
init_timer(&mytimer);  
 mytimer ->timer.expires = jiffies + 5*HZ;
 mytimer ->timer.data = (unsigned long) dev;
 mytimer ->timer.function = &corkscrew_timer; /* timer handler */

Definieren Sie dynamisch einen Timer über init_timer() und binden Sie dann die Adresse und Parameter der Verarbeitungsfunktion an eine timer_list,
Beachten Sie, dass unabhängig davon, welche Methode zur Initialisierung verwendet wird, das Wesentliche nur darin besteht, den Feldern Werte zuzuweisen, sodass die Ablauf-, Funktions- und Datenfelder direkt vor dem Ausführen von add_timer () geändert werden können.
Die Definitionen der oben genannten Makros und Funktionen finden Sie unter include/linux/timer.h.

Registrieren

Damit der Timer wirksam wird, muss er mit einer dedizierten verknüpften Liste im Kernel verbunden werden. Dies kann durch add_timer(struct timer_list *timer) erreicht werden.

Neu anmelden

Um die Planungszeit eines Timers zu ändern, können Sie mod_timer(struct timer_list *timer, unsigned long Expires) aufrufen. mod_timer() registriert den Timer erneut beim Kernel, unabhängig davon, ob die Timer-Funktion ausgeführt wurde.

Abmelden

Um die Registrierung eines Timers aufzuheben, können Sie del_timer(struct timer_list *timer) oder del_timer_sync(struct timer_list *timer) verwenden. Unter anderem wird del_timer_sync auf SMP-Systemen verwendet (auf Nicht-SMP-Systemen entspricht es del_timer). Wenn die abzumeldende Timer-Funktion auf einer anderen CPU ausgeführt wird, wartet del_timer_sync (), bis die Ausführung abgeschlossen ist Die Funktion wird in den Ruhezustand versetzt. Darüber hinaus sollte vermieden werden, dass mit der geplanten Funktion um dieselbe Sperre konkurriert wird. Für einen Timer, der bereits ausgeführt wurde und sich nicht neu registriert hat, hat die Funktion zum Aufheben der Registrierung eigentlich nichts zu tun.

int timer_pending(const struct timer_list *timer)
Mit dieser Funktion wird ermittelt, ob ein Timer zur Kernelliste hinzugefügt wurde, der auf die geplante Ausführung wartet. Beachten Sie, dass der Kernel den entsprechenden Timer aus der Kernel-verknüpften Liste löscht, wenn eine Timer-Funktion ausgeführt werden soll (entspricht einem Abmelden)

一个简单的例子

#include 
\#include 
\#include 

struct timer_list mytimer;

static void myfunc(unsigned long data)
{
    printk("%s/n", (char *)data);
    mod_timer(&mytimer, jiffies + 2*HZ);
}

static int __init mytimer_init(void)
{
    setup_timer(&mytimer, myfunc, (unsigned long)"Hello, world!");
    mytimer.expires = jiffies + HZ;
    add_timer(&mytimer);

​    return 0;
}

static void __exit mytimer_exit(void)
{
    del_timer(&mytimer);
}

module_init(mytimer_init);
module_exit(mytimer_exit);

例子2
static struct timer_list power_button_poll_timer;

static void power_button_poll(unsigned long dummy)
{
 if (gpio_line_get(N2100_POWER_BUTTON) == 0) {
 ctrl_alt_del();
 return;
 }

 power_button_poll_timer.expires = jiffies + (HZ / 10);
 add_timer(&power_button_poll_timer);
}


static void __init n2100_init_machine(void)
{
;
;
 init_timer(&power_button_poll_timer);
 power_button_poll_timer.function = power_button_poll;
 power_button_poll_timer.expires = jiffies + (HZ / 10);
 add_timer(&power_button_poll_timer);
}

例子3

设备open时初始化和注册定时器

static int corkscrew_open(struct net_device *dev)

{
;
;
 init_timer(&vp->timer);  
 vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
 vp->timer.data = (unsigned long) dev;
 vp->timer.function = &corkscrew_timer; /* timer handler */
 add_timer(&vp->timer);
:
;
}
定时器超时处理函数,对定时器的超时时间重新赋值

static void corkscrew_timer(unsigned long data)
{
;
;
  vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
  add_timer(&vp->timer);
;
;
}

设备close时删除定时器
static int corkscrew_close(struct net_device *dev)
{
 ;
;
 del_timer(&vp->timer);
;
;
}


例子4

本例子用DEFINE_TIMER静态创建定时器

\#include 
\#include 
\#include 
\#include 
\#include 
\#include 

static void ledtrig_ide_timerfunc(unsigned long data);

DEFINE_LED_TRIGGER(ledtrig_ide);
static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);
static int ide_activity;
static int ide_lastactivity;

void ledtrig_ide_activity(void)
{
    ide_activity++;
    if (!timer_pending(&ledtrig_ide_timer))
       mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
}
EXPORT_SYMBOL(ledtrig_ide_activity);

static void ledtrig_ide_timerfunc(unsigned long data)
{
    if (ide_lastactivity != ide_activity) {
       ide_lastactivity = ide_activity;
       led_trigger_event(ledtrig_ide, LED_FULL);
       mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
    } else {
       led_trigger_event(ledtrig_ide, LED_OFF);
    }
}

static int __init ledtrig_ide_init(void)
{
    led_trigger_register_simple("ide-disk", &ledtrig_ide);
    return 0;
}

static void __exit ledtrig_ide_exit(void)
{
    led_trigger_unregister_simple(ledtrig_ide);
}

module_init(ledtrig_ide_init);
module_exit(ledtrig_ide_exit);

MODULE_AUTHOR("Richard Purdie ");
MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
MODULE_LICENSE("GPL");

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Verwendung von Linux-Kernel-Timern. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:lxlinux.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen