Maison  >  Article  >  Tutoriel système  >  Explication détaillée de l'utilisation des timers du noyau Linux

Explication détaillée de l'utilisation des timers du noyau Linux

WBOY
WBOYavant
2024-02-09 13:50:27997parcourir

Le minuteur du noyau LINUX est un mécanisme utilisé par le noyau pour contrôler la planification et l'exécution d'une certaine fonction à un certain moment dans le futur (en fonction des jiffies). L'implémentation de ce mécanisme se trouve dans les fichiers et kernel/timer.c.

Explication détaillée de lutilisation des timers du noyau Linux

La fonction planifiée doit être exécutée de manière asynchrone. Elle est similaire à une "interruption logicielle" et se situe dans un contexte non-processus. La fonction de planification doit donc obéir aux règles suivantes :

  1. Sans le pointeur actuel, aucun accès à l'espace utilisateur n'est autorisé. Puisqu'il n'y a pas de contexte de processus, le code concerné n'a aucun lien avec le processus interrompu.
  2. La mise en veille ou les fonctions et programmes susceptibles de provoquer la mise en veille ne peuvent pas être exécutés.
  3. Toute structure de données accessible doit être protégée contre les accès simultanés afin d'éviter les conditions de concurrence.

Une fois que la fonction de planification du minuteur du noyau a été exécutée une fois, elle ne sera plus exécutée (équivalent à une déconnexion automatique). Cependant, il est possible de s'exécuter périodiquement en se reprogrammant dans le cadre de la fonction planifiée.

Dans un système SMP, la fonction de planification s'exécute toujours sur le même processeur sur lequel elle a été enregistrée pour obtenir autant que possible la localité du cache.

API Minuterie

Structure des données du minuteur du noyau

struct timer_list {
  struct list_head entry;

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

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

Le champ expire représente la valeur jiffies que le minuteur est censé exécuter. Lorsque la valeur jiffies est atteinte, la fonction sera appelée et les données seront transmises en paramètre. Lorsqu'un timer est enregistré dans le noyau, le champ de saisie est utilisé pour connecter le timer à une liste chaînée du noyau. Le champ de base est utilisé en interne par l'implémentation du noyau.
Notez que la valeur d'expiration est de 32 bits, car les minuteries du noyau ne s'appliquent pas aux instants futurs longs.

Initialisation

Avant d'utiliser struct timer_list, vous devez initialiser la structure des données pour vous assurer que tous les champs sont correctement définis. Il existe deux méthodes d'initialisation.
Méthode 1 :
DEFINE_TIMER(timer_name, function_name, expires_value, data);
Cette macro créera statiquement un minuteur de noyau nommé timer_name et initialisera ses champs de fonction, d'expiration, de nom et de base.

Méthode 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 */

Définissez dynamiquement une minuterie via init_timer(), puis liez l'adresse et les paramètres de la fonction de traitement à une timer_list,
Notez que quelle que soit la méthode utilisée pour initialiser, l'essence est simplement d'attribuer des valeurs aux champs, de sorte que les champs d'expiration, de fonction et de données peuvent être modifiés directement avant d'exécuter add_timer().
Pour les définitions des macros et fonctions ci-dessus, voir include/linux/timer.h.

Inscrivez-vous

Pour que le timer prenne effet, il doit être connecté à une liste chaînée dédiée dans le noyau. Cela peut être réalisé via add_timer(struct timer_list *timer).

Ré-inscrire

Pour modifier l'heure de programmation d'un timer, vous pouvez appeler mod_timer(struct timer_list *timer, unsigned long expires). mod_timer() réenregistrera le timer dans le noyau, que la fonction timer ait été exécutée ou non.

Déconnexion

Pour désenregistrer une minuterie, vous pouvez utiliser del_timer(struct timer_list *timer) ou del_timer_sync(struct timer_list *timer). Parmi eux, del_timer_sync est utilisé sur les systèmes SMP (sur les systèmes non-SMP, il est égal à del_timer). Lorsque la fonction de minuterie à déconnecter est en cours d'exécution sur un autre processeur, del_timer_sync() attendra qu'elle termine son exécution, donc ceci la fonction hibernera. De plus, il faut également éviter de rivaliser pour le même verrou avec la fonction programmée. Pour un timer qui a déjà été exécuté et qui ne s'est pas réenregistré, la fonction de désenregistrement n'a en réalité rien à faire.

int timer_ending(const struct timer_list *timer)
Cette fonction est utilisée pour déterminer si un minuteur a été ajouté à la liste du noyau en attente d'exécution planifiée. Notez que lorsqu'une fonction de minuterie est sur le point d'être exécutée, le noyau supprimera la minuterie correspondante de la liste liée du noyau (équivalent à une déconnexion)

一个简单的例子

#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");

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer