• linux 内核等待队列


    内核调度器管理要运行的任务列表,被称为运行队列。睡眠的进程不再被调度,除非被唤醒。进入睡眠等待的进程,可以释放处理器。

    等待队列

    等待队列实际上用于处理被阻塞的IO,以等待特定条件成立,并感知数据或资源的可用性。

    // include/linux/wait.h
    
    struct wait_queue_entry {
    	unsigned int		flags;
    	void			*private;
    	wait_queue_func_t	func;
    	struct list_head	entry;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    entry这个字段是将进程加入到等待队列的链表中,每个进程都会进入睡眠状态,直到条件变为真。等待队列可以被看作简单的进程链表和锁。

    // 静态声明
    DECLARE_WAIT_QUEUE_HEAD(name);
    
    // 动态声明
    wait_queue_head_t my_wait_queue;
    init_waitqueue_head(&my_wait_queue);
    
    // 阻塞
    // 如果条件为false,则阻塞等待队列中的当前任务(进程)。
    int wait_event_interruptible(wait_queue_head_t q, CONDITION);
    /**
     * wait_event_interruptible - sleep until a condition gets true
     * @wq_head: the waitqueue to wait on
     * @condition: a C expression for the event to wait for
     *
     * The process is put to sleep (TASK_INTERRUPTIBLE) until the
     * @condition evaluates to true or a signal is received.
     * The @condition is checked each time the waitqueue @wq_head is woken up.
     *
     * wake_up() has to be called after changing any variable that could
     * change the result of the wait condition.
     *
     * The function will return -ERESTARTSYS if it was interrupted by a
     * signal and 0 if @condition evaluated to true.
     */
     // 一直睡眠直到条件变为真。
     // 在条件变为true或者收到信号之前,进程一直处于可中断睡眠状态(TASK_INTERRUPTIBLE)。
     // 条件在每次唤醒等待队列时被检查。
     // wake_up 一般在条件在相关变量变化时被被调用。
     // 如果函数被信号中断,则该函数将返回 -ERESTARTSYS,如果@condition计算结果为 true,则返回 0。
    #define wait_event_interruptible(wq_head, condition)                            \
    ({                                                                              \
            int __ret = 0;                                                          \
            might_sleep();                                                          \
            if (!(condition))                                                       \
                    __ret = __wait_event_interruptible(wq_head, condition);         \
            __ret;                                                                  \
    })
    
    
    // 解除阻塞
    // 如果上述条件为true,则唤醒在等待队列中休眠的进程。
    void wake_up_interruptible(wait_queue_head_t *q);
    #define wake_up_interruptible(x)        __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
    /**
     * __wake_up - wake up threads blocked on a waitqueue.
     * @wq_head: the waitqueue
     * @mode: which threads
     * @nr_exclusive: how many wake-one or wake-many threads to wake up
     * @key: is directly passed to the wakeup function
     *
     * It may be assumed that this function implies a write memory barrier before
     * changing the task state if and only if any tasks are woken up.
     */
    void __wake_up(struct wait_queue_head *wq_head, unsigned int mode,
                            int nr_exclusive, void *key)
    {
            __wake_up_common_lock(wq_head, mode, nr_exclusive, 0, key);
    }
    EXPORT_SYMBOL(__wake_up);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    wait_event_interruptible 不会持续轮询,而只是在被调用时评估条件。

    • 如果条件为假,则进程将进入TASK_INTERRUPTIBLE 状态并从运行队列中删除,之后每次在等待队列中调用wait_up_interruptible时,都会重新检查条件。
    • 如果wait_up_interruptible运行时发现条件为真,则等待队列中的进程将被唤醒。并将进程状态设置为TASK_RUNNING。进程按照他们进入睡眠的顺序唤醒(先进先出),要唤醒等待队列中等待的所有进程,应该使用wake_up_interruptible_all
    #define wake_up_interruptible_all(x)    __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
    
    • 1

    如果调用wake_upwake_up_interruptible,条件为false,则什么都不会发生。如果没有显式调用,进程将永远不会被唤醒。

    等待队列的测试程序

    // wait_queue.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    static DECLARE_WAIT_QUEUE_HEAD(my_wq);
    static int condition = 0;
    
    /* 声明一个工作队列 */
    static struct work_struct work;
    
    static void work_handler(struct work_struct *work)
    {
            printk("Workqueue module handler!\n");
            msleep(5000);
            printk("Wake up the sleeping module!\n");
            condition = 1;
            wake_up_interruptible(&my_wq);
    }
    
    static int __init test_init(void)
    {
            printk("Wait queue test module!\n");
    
            INIT_WORK(&work, work_handler);
            schedule_work(&work);
    
            printk("Going to sleep %s!\n", __func__);
            wait_event_interruptible(my_wq, condition != 0);
    
            printk("Woken up by work job!\n");
            return 0;
    }
    
    void test_exit(void)
    {
            printk("Waitqueue example cleanup!\n");
    }
    
    module_init(test_init);
    module_exit(test_exit);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    [25596.170555] Wait queue test module!
    [25596.170560] Going to sleep test_init!
    [25596.170565] Workqueue module handler!
    [25601.200917] Wake up the sleeping module!
    [25601.200931] Woken up by work job!
    
    • 1
    • 2
    • 3
    • 4
    • 5

    内核还提供了一个函数,当condtion或者时延时时间到了,等待队列中的进程恢复运行。

    /**
     * wait_event_interruptible_timeout - sleep until a condition gets true or a timeout elapses
     * @wq_head: the waitqueue to wait on
     * @condition: a C expression for the event to wait for
     * @timeout: timeout, in jiffies
     *
     * The process is put to sleep (TASK_INTERRUPTIBLE) until the
     * @condition evaluates to true or a signal is received.
     * The @condition is checked each time the waitqueue @wq_head is woken up.
     *
     * wake_up() has to be called after changing any variable that could
     * change the result of the wait condition.
     *
     * Returns:
     * 0 if the @condition evaluated to %false after the @timeout elapsed,
     * 1 if the @condition evaluated to %true after the @timeout elapsed,
     * the remaining jiffies (at least 1) if the @condition evaluated
     * to %true before the @timeout elapsed, or -%ERESTARTSYS if it was
     * interrupted by a signal.
     */
    #define wait_event_interruptible_timeout(wq_head, condition, timeout)		\
    ({										\
    	long __ret = timeout;							\
    	might_sleep();								\
    	if (!___wait_cond_timeout(condition))					\
    		__ret = __wait_event_interruptible_timeout(wq_head,		\
    						condition, timeout);		\
    	__ret;									\
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    这里的timeout用的单位是jiffies,所以需要有个函数,将延时时间转换jiffies

    unsigned int jiffies_to_msecs(const unsigned long j);
    unsigned int jiffies_to_usecs(const unsigned long j);
    unsigned long msecs_to_jiffies(const unsigned int m);
    unsigned long usecs_to_jiffies(const unsigned int u);
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    GPT系列论文解读:GPT-3
    python多分支选择结构实例讲解
    Tomcat之startup.bat启动闪退解决
    RabbitMQ-02(docker安装、RabbitMQ的角色分类、AMQP、消息确认机制ACK)
    springMVC 文件上传和下载
    基于Python的图像加密算法实现
    MyBatis
    国赛高教杯使用python/matlab必会算法数学建模-回归分析模块(课程6)
    详解C++静态多态和动态多态的区别
    分享一个通用的so动态库的编译方法
  • 原文地址:https://blog.csdn.net/qq_42931917/article/details/127820157