当前位置:   article > 正文

linux内核 等待队列的使用_linux wait queue的使用

linux wait queue的使用

阻塞与非阻塞是设备访问的两种方式。驱动程序需要提供阻塞(等待队列,中断)和非阻塞方式(轮询,异步通知)访问设备。在写阻塞与非阻塞的驱动程序时,经常用到等待队列。

等待队列

阻塞访问最大的好处就是当设备文件不可操作的时候进程可以进入休眠态,这样可以将cpu资源让出来。但是,当设备文件可以操作的时候就必须唤醒进程,一般在中断函数里面完成唤醒工作。Linux内核提供了等待队列(wait queue)来实现阻塞进程的唤醒工作,如果我们要在驱动使用等待队列,必须创建并初始化一个等待队列头。

等待队列头

struct __wait_queue_head {
	spinlock_t lock;
	struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head+t;
  • 1
  • 2
  • 3
  • 4
  • 5

定义好等待队列头以后需要初始化,使用init_waitqueue_head函数初始化,函数原型如下:

void init_waitqueue_head(wait_queue_head_t *q);
  • 1

参数q就是要初始化的等待队列头。
也可以使用宏DECLARE_WAIT_QUEUE_HEAD来一次性完成等待队列头的定义和初始化。

等待队列项

等待队列头就是一个等待队列的头部,每个访问设备的进程都是一个队列项,当设备不可用的时候就要将这些进程对应的等待队列项添加到等待队列里面。结构体如下:

struct __wait_queue {
	unsigned int flags;
	void *private;
	wait_queue_func_t func;
	struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用宏DECLARE_WAITQUEUE定义并初始化一个等待队列项,宏的内容如下:

DECLARE_WAITQUEUE(name,tsk);
  • 1

name就是等待队列项的名字,tsk表示这个等待队列项属于哪个任务(进程),一般设置为current,在Linux内核中current相当于一个全局变量,表示当前进程。因此宏DECLARE_WAITQUEUE就是给当前正在运行的进程创建并初始化了一个等待队列项。

将队列项添加/移除等待队列头

当设备不可访问的时候就需要将进程对应的等待队列项添加到前面创建的等待队列头中,只有添加到等待队列头中以后的进程才能进入休眠态。当设备可以访问以后再将进行对应的等待队列项从等待队列头中移除即可,等待队列项添加API函数如下:

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
  • 1

q:等待队列项要加入的等待队列头
wait:要加入的等待队列项
等待队列项移除API

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
  • 1

q:要删除的等待队列项所处的等待队列头
wait:要删除的等待队列项

等待唤醒

当设备可以使用的时候就要唤醒进入休眠态的进程

void wake_up(wait_queue_head_t *q);
void wake_up_interruptible(wait_queue_head_t *q);
  • 1
  • 2

参数q就是要唤醒的等待队列头,这两个函数会将这个等待队列头中的所有进程都唤醒。wake_up函数可以唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的进程,而wake_up_interruptible函数只能唤醒处于TASK_INTERRUPTIBLE状态的进程。

等待事件

除了主动唤醒以外,也可以设置等待队列等待某个事件,当这个事件满足以后就自动唤醒等待队列中的进程,和等待事件有关的API函数如下:

函数描述
wait_event(wq, condition)等待以 wq 为等待队列头的等待队列被唤醒,前提是 condition 条件必须满足(为真),否则一直阻塞 。 此 函 数 会 将 进 程 设 置 为TASK_UNINTERRUPTIBLE 状态
wait_event_timeout(wq, condition, timeout)功能和 wait_event 类似,但是此函数可以添加超时时间,以 jiffies 为单位。此函数有返回值,如果返回 0 的话表示超时时间到,而且 condition为假。为 1 的话表示 condition 为真,也就是条件满足了。
wait_event_interruptible(wq, condition)与 wait_event 函数类似,但是此函数将进程设置为 TASK_INTERRUPTIBLE,就是可以被信号打断。
wait_event_interruptible_timeout(wq,
condition, timeout)与 wait_event_timeout 函数类似,此函数也将进程设置为 TASK_INTERRUPTIBLE,可以被信号打断。

具体使用方法

方法一 使用等待事件

读取按键值程序休眠实例

定义等待队列头

wait_queue_head_t r_wait; /* 读等待队列头*/
  • 1

初始化队列头

init_waitqueue_head(&r_wait);
  • 1

在读取按键值的函数中使用等待事件,起到阻塞效果

wait_event(r_wait, condition);/*等待按键有效*/
  • 1

在按键发生时进入的函数中使用等待唤醒函数

if(condition)
	wake_up(&r_wait);
  • 1
  • 2

方法二 使用等待队列项

定义等待队列项

wait_queue_head_t r_wait; /* 读等待队列头*/
init_waitqueue_head(&r_wait);/*初始化等待队列头*/
DECLARE_WAITQUEUE(wait, current); /*定义一个等待队列项*/
  • 1
  • 2
  • 3

在读取按键值的函数中,将队列项添加到等待队列头

add_wait_queue(&r_wait, &wait);/*将等待队列项添加到等待队列头*/
__set_current_state(TASK_INTERRUPTIBLE);/*当前进程设置为可被打断的状态*/
schedule();/*切换状态*/

/*唤醒以后从这里运行,判断是否被信号唤醒*/
if(signal_pending(current)) {
	//被信号唤醒,做相应操作
	goto data_error;
}

data_error:
	__set_current_state(TASK_RUNNING);	/*将当前任务设置为运行状态*/
	remove_wait_queue(&r_wait, &wait);	/*将对应的队列项从等待队列头删除*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在按键发生时进入的函数中使用等待唤醒函数

wake_up_interruptible(&r_wait);
  • 1

看到这的小伙伴,帮点个赞呗~~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/601609
推荐阅读
相关标签
  

闽ICP备14008679号