linux内核中的等待队列的基本操作

   在linux内核中进程的状态主要有几种状态:

   1.运行态:即进程正在CPU上进行运行,它此刻正在占有CPU;

   2.就绪态:即进程除了CPU之外,已经具备了运行的所有条件,在就绪队列中等待调度器(schedule)的调度;

   3.阻塞态:即进程除了缺少CPU外,还缺少其他条件,在等待队列中等待所需要的条件;


   介绍linux内核中的等待队列的组织结构以及各种对等待队列的操作:

   等待队列由两部分构成:等待队列头 + 等待队列中的等待项;

   等待队列头的结构:

      struct __wait_queue_head

            {

                  spinlock_t lock;  // 自选锁,用于保护等待队列,防止并发访问

                  struct list_head task_list;//用于构建等待队列

            }

     typedef struct __wait_queue_head wait_queue_head_t;


等待队列中的等待项:

   struct __wait_queue

      {

        unsigned int flags;

        void *private;

        wait_queue_func_t func;

        struct list_head task_list;

      }

每个字段的介绍:

    flags : 用于判断当等待条件满足时,是唤醒等待队列中的等待该条件的所有

            等待进程,还是只唤醒一个;

            flags 的最后一位为 1  时表示每次只唤醒一个等待队列中的进程;

            flags 的最后一位为 0 时表示每次唤醒等待该条件的所有进程;

            #define WQ_FLAG_EXCLUSIVE 0x01


    private: 用于存放 struct task_struct 的地址;

    wait_queue_func_t :等待队列中的进程被唤醒的方式;

         typedef int (*wait_queue_func_t)(wait_queue_t *wait,unsigned mode,

                                          int flags, void *key)


等待的队列的结构图:


对等待队列的各种操作:

  1.等待队列头的静态初始化:

     #define __WAIT_QUEUE_INITIALIZER(name) {         \

      .lock = __SPIN_LOCK_UNLOCK(name.lock),// lock = 1;  \

      .task_list = {&(name).task_list, &(name).task_list} }


     #define DECLARE WAIT_QUEUE_HEAD(name) \

           wait_queue_head_t name = __WAIT_QUEUE_INITIALIZER(name)


      所以可以直接通过 : DECLARE_WAIT_QUEUE_HEAD(name) 来完成一个等待队列头的定义以及初始化操作;


2. 等待队列头的动态初始化操作:

    void __init_waitqueue_head(wait_queue_head_t *p, struct lock_class_key *key)

    {

         spin_lock_init(&p->lock); // 初始化spinlock_t lock lock = 1;

         INIT_LIST_HEAD(&p->task_list);// 初始化链表头;

    }

    #define init_waitqueue_head(name) \

        do {    \

             static struct lock_class_key  __key; \

             __init_wait_queue_head((name), &__key);\

           } while(0)

    所以如果使用 init_waitqueue_head()的用法为:

           wait_queue_head_t name;

           init_waitqueue_head(&name);


DECLARE_WAIT_QUEUE_HEAD() 与 init_waitqueue_head()的区别:

     1.DECLARE_WAIT_QUEUE_HEAD完全时用宏定义实现的,因此程序在编译的时候就完成了变量的定义与初始化。而 init_waitqueue_head()本质上来说是对 __init_waitqueue_head()的一次封装。虽然使用了宏定义,其仍然是一个函数,它对变量的初始化是在程序运行的时候进行的;


3.对等待队列中的等待项进行静态定义与初始化:

    #define __WAITQUEUE_INITIALIZER(name, task) {  \

         .private   = task, \

         .func      = default_wake_function,  \

         .task_list = {NULL,NULL} }


    #define DECLARE_WAITQUEUE(name,task)     \

         wait_queue_t name = __WAITQUQUE_INITIALIZER(name, task)

     相当与:

         wait_queue_t entry;

         struct task_struct task;

         entry = {

             .private = &task,

             .func    = default_wake_function,

             .taks_list = {NULL, NULL}

                 }


4.动态初始化等待队列中的数据项:

   static inline void init_waitqueue_entry(wait_queue_t *p, struct task_struct    *task)

   {

       p->flags   = 0;

       p->private = task;

       p->func    = default_wake_function;

      

   }

  用法:wait_queue_t wait;

       struct task_struct task;

        init_waitqueue_entry(&wait, &task);


5.使用自定义的函数作为等待队列中的进程唤醒做处理:

   static inline void init_waitqueue_func_entry(wait_queue_t *wait,

                      wait_queue_func_t func)

   {

       p->flags = 0;

       p->private = NULL;

       p->func    = func;

   }


6.判断一个等待队列是否可用:

   static inline int waitqueue_active(wait_queue_head_t *p)

   {

      return !list_empty(&p->task_list);

   }

   waitqueue_active()本质上实在判断一个等待队列是否为空。

         如果为空的话,等待队列不可用返回 0;

         如果不为空,等待队列可用返回 1;


7.向等待队列中添加一个数据项:

    static inline void __add_wait_queue(wait_queue_head_t *head,

                                        wait_queue_t *new)

    {

        list_add(&new->task_list, &head->task_list);

    }


    void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

    {

        unsigned int flags ;

        wait->flags &= ~WQ_FLAG_EXCLUSIVE; // define WQ_FLAG_EXCLUSIVE 0x01

        spin_lock_irqsave(&q->lock, flags);

        __add_wait_queue(q, wait);

        spin_unlock_irqstore(&q->lock, flags);

    }

   

   所以 add_wait_queue()是__add_wait_queue()的封装,并且加入了一些保护等待队列防止并发访问的措施, 所以在向等待队列中添加元素时使用 add_wait_queue()会更安全;



8.向等待队列中添加一个每次只能唤醒一个数据项:

    static inline void __add_wait_queue_tail(wait_queue_head_t *head,

                                             wait_queue_t *new)

  {

      list_add_tail(&new->task_list, &head->task_list);

  }


   void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait)

   {

       unsigned int flags ;

       wait->flags |=  WQ_FLAG_EXCLUSIVE;

       spin_lock_irqsave(&q->lock, flags);

       __add_wait_queue_tail(q,wait);

       spin_unlock_irqstore(&q->lock, flags);

   }


 可以看出 add_wait_queue(wait_queue_head_t *head, wait_queue_t *wait)

          每次向等待队列的头后面添加一个非互斥唤醒的等待项;

         add_wait_queue_exclusive(wait_queue_head_t *head, wait_queue_t *wait)

          每次向等待队列的尾部添加一个互斥唤醒的等待项;

  


9.将一个等待项从等待队列中删除:

   static inline void __remove_wait_queue(wait_queue_head_t *head,

                                          wait_queue_t *wait)

   {

       list_del(&wait->task_list);

   }

   void remove_wait_queue(wait_queue_head_t *head, wait_queue_t *wait)

   {

       unsigned int flags;

       spin_lock_irqsave(&head->lock, flags);

       __remove_wait_queue(head,wait);

       spin_unlock_irqstore(&head->lock, flags);

   }


 小总结: add_wait_queue();   EXPORT_SYMBOL(add_wait_queue);

          add_wait_queue_exclusive();  EXPORT_SYMBOL(add_wait_queue_exclusive);

          remove_wait_queue(); EXPORT_SYMBOL(remove_wait_queue);

     这三个对等待队列的操作是内核提过的函数,



    内核中的等待队列中的进程是需要在一定条件下给唤醒的,然后加入到就绪队列中的。以及正在运行的进程如何将其加入到等待队列之中的。

    内核也提供了一些函数,帮助我们将进程正在运行的进程阻塞,让后将其加入到等待队列中。以及在等待队列中唤醒唤醒阻塞进程。


阻塞正在运行的进程,然后将其加入到等待队列中的函数:

  1. void prepare_to_wait(wait_queue_head_t *head, wait_queue_t *wait, int state)

     state :表示进程的状态

            取值 TASK_RUNNING, TASK_INTERRUPTIBLE, TASK_UNINTERRUPTIBLE


    void prepare_to_wait(wait_queue_head_t *head, wait_queue_t *wait, int state)

    {

       unsigned int flags;

       wait->flags &= ~WQ_FLAG_EXCLUSIVE; // 非互斥唤醒进程;

       spin_lock_irqsave(&head->lock, flags);

       if( list_empty(&wait->task_list) ) // 如果为空,说明其之前并不在等待对列中

          __add_wait_queue(head, wait);  // 将wait entry 加入到等待队列之中

       set_current_state(state);   // 使用 set_current_state()改变进程的运行状态

       spin_unlock_irqstore(&head->lock, flags);

    }


    EXPORT_SYMBOL(prepare_to_wait);


    void prepare_to_wait_exclusive(wait_queue_head_t *head,

                                   wait_queue_head *wait, int state)

    {

       unsigned int flags;

       wait->flags |= WQ_FLAG_EXCLUSIVE;  // 互斥唤醒进程;

       spin_lock_irqsave(&head->lock, flags);

       if(  list_empty(&wait->task_list) )

          __add_wait_queue_tail(head, wait); //添加对尾

       set_current_state(state);

       spin_unlock_irqstore(&head->lock, flags);

    }

   EXPORT_SYMBOL(prepare_to_wait_exclusive);

  

从阻塞队列中删除一个等待进程的函数:

  void  finish_wait(wait_queue_head_t *head, wait_queue_t *wait)

  {

      unsigned int flags;

      __set_current_state(TASK_RUNNING); //将当前的进程状态改为运行态;

      if( !list_empty_careful(&wait->task_list) ) // 确定进程在等待队列之中

      {

         spin_lock_irqsave(&head->lock, flags);

         list_del_init(&wait->task_list);

         spin_unlock_irqstore(&head->lock, flags);

      }

  }


    EXPORT_SYMBOL(finish_wait);



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。