文章目录
前言
软硬件环境:
硬件:PC
软件:ubuntu18.04
本文主要介绍,Linux 内核中的等待队列, 包括它的含义,相关数据结构和函数及使用场景和实例等。
一、什么是等待队列
等待队列是内核中一个重要的数据结构。当以阻塞方式访问设备时,如果设备不可操作,那么进程就会进入休眠状态。等待队列就是来完成进程休眠操作的一种数据结构。
等待队列用于使得进程等待某一特定事件的发生,无需频繁的轮询,进程在等待周期中睡眠,当事件发生后由内核自动唤醒。
等待队列是一种实现阻塞和唤醒的内核机制,很早就作为一个基本的功能单位出现在Linux内核中,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。
二、等待队列相关的数据结构和函数接口
1. 等待队列相关数据结构
- wait_queue_head_t
如下图所示, 是数据结构wait_queue_head_t的定义
2. 等待队列相关函数接口
-
定义和初始化等待队列头
初始化等待队列头有两种方式:静态和动态
静态:
DECLARE_WAIT_QUEUE_HEAD(wait); //静态初始化一个等待队列头 wait
动态:
wait_queue_head_t wait;
init_waitqueue_head(&wait); -
休眠进程
wait_event(wait, condition) //将当前进程的状态设置为 TASK_UNINTERRUPTIBLE
wait_event_interruptible(wait, condition) //TASK_INTERRUPTIBLE
wait_event_timeout(wait, condition, timeout) //TASK_UNINTERRUPTIBLE
wait_event_interruptible_timeout(wait, condition, timeout) //TASK_INTERRUPTIBLE
说明:
- wait 是等待队列头, condition 是条件,如果调用 wait_event()前 condition == 0 ,则调用 wait_event 之后,当前进程就会休眠, 如果调用wait_event()前,condition == 1, 则 wait_event()会立即返回;
- wait_event()与wait_event_interruptible()的区别在于后者的休眠是可以被信号打断,而前者则不会。别的进程发来一个信号比如kill ,TASK_INTERRUPTIBLE 就会被换醒来去处理。然而 TASK_UNINTERRUPTIBLE则不会被唤醒,继续保持睡眠;
- wait_event_timeout() 当阻塞等待的超时时间到达时,无论condition是否满足,均会立即返回;
- 唤醒进程
wake_up(wait_queue_head_t *wait)
wake_up_interruptible(wait_queue_head_t *wait)
说明:
- wake_up()和wake_up_interruptible()会唤醒以wait作为等待队列头的进入休眠的进程,唤醒之后,它会判断
condition 是否为真,如果还是假的则继续睡眠; - wake_up()应与wake_event() 或 wait_event_timeout()成对使用, 而wake_up_interruptible()应与wait_event_interruptible()或 wait_event_interruptibel_timeout()成对使用,wake_up()可唤醒处于TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE状态的进程,而wake_up_interruptible()只能唤醒处于TASK_INTERRUPTIBLE状态的进程;
三、等待队列的使用实例
1. 等待队列在V4L2子系统中的使用实例
- 阻塞操作
在V4L2子系统中, 当应用程序调用ioctl (DQBUF)操作的时候,最终会通过如下调用顺序调用到__vb2_wait_for_done_vb()函数
vb2_core_dqbuf()-------->__vb2_get_done_vb()--------->__vb2_wait_for_done_vb()
在__vb2_wait_for_done_vb()函数中会调用等待队列相关的阻塞接口wait_event_interruptible() 等待被唤醒
2. 唤醒操作
当硬件有数据传递到SOC端的时候, 会产生硬件中断,进入中断处理函数, 在中断处理函数中驱动会调用**vb2_buffer_done()**函数, 唤醒之前等待数据的等待队列, 如下图所示.
在vb2_buffer_done()函数中会去调用wake_up()函数,进行唤醒操作。
2. 最简单的等待队列使用实例
如下所示,为最简单的等待队列使用实例
wait_queue_demo.c
#include <linux/init.h>
#