Linux内核中休眠与唤醒的使用(wait_eventwake_up)
Posted ZHONGCAI0901
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核中休眠与唤醒的使用(wait_eventwake_up)相关的知识,希望对你有一定的参考价值。
1. 前言
在wait_event_interruptible()
函数中会将当前进程的状态设置成TASK_INTERRUPTIBLE,然后调用schedule()
,它会将位于TASK_INTERRUPTIBLE状态的进程从run queue队列中删除。从run queue队列中删除的结果是,当前这个进程将不再参与调度,除非通过其他将这个进程重新放入run queue队列中,wake_up()
函数就是这个作用。
由于这一段代码位于一个由condition
控制的for(;;)
循环中,所以当由shedule()
返回时(当然是被wake_up()
之后,通过其他进程的schedule()
而 再次调度本进程),如果条件condition
不满足,本进程将自动再次被设置为TASK_INTERRUPTIBLE状态,接下来执行schedule()
的结果是再次被 从run queue队列中删除。这时候就需要再次通过wake_up()
重新添加到 run queue队列中。
如此反复,直到condition
为真的时候被wake_up()
。
#define ___wait_event(wq, condition, state, exclusive, ret, cmd) \\
( \\
__label__ __out; \\
wait_queue_t __wait; \\
long __ret = ret; /* explicit shadow */ \\
\\
init_wait_entry(&__wait, exclusive ? WQ_FLAG_EXCLUSIVE : 0); \\
for (;;) \\
long __int = prepare_to_wait_event(&wq, &__wait, state);\\
\\
if (condition) \\
break; \\
\\
if (___wait_is_interruptible(state) && __int) \\
__ret = __int; \\
goto __out; \\
\\
\\
cmd; \\
\\
finish_wait(&wq, &__wait); \\
__out: __ret; \\
)
#define __wait_event_interruptible(wq, condition) \\
___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \\
schedule())
#define wait_event_interruptible(wq, condition) \\
( \\
int __ret = 0; \\
might_sleep(); \\
if (!(condition)) \\
__ret = __wait_event_interruptible(wq, condition); \\
__ret; \\
)
2. 休眠和唤醒内核函数介绍
- 休眠函数
参考内核源码:include\\linux\\wait.h
。
函数 | 说明 |
wait_event_interruptible(wq, condition) | 休眠,直到 condition 为真; 休眠期间是可被打断的,可以被信号打断 |
wait_event(wq, condition) | 休眠,直到 condition 为真; 退出的唯一条件是 condition 为真,信号也不好使 |
wait_event_interruptible_timeout (wq, condition, timeout) | 休眠,直到 condition 为真或超时; 休眠期间是可被打断的,可以被信号打断 |
wait_event_timeout(wq, condition, timeout) | 休眠,直到 condition 为真; 退出的唯一条件是 condition 为真,信号也不好使 |
比较重要的参数就是:
1. wq:waitqueue,等待队列
休眠时除了把程序状态改为非 RUNNING 之外,还要把进程/进程放入 wq 中,以后中断服务程序要从 wq 中把它取出来唤醒。
2. condition
这可以是一个变量,也可以是任何表达式。表示“一直等待,直到 condition 为真”
- 唤醒函数
参考内核源码:include\\linux\\wait.h
。
函数 | 说明 |
wake_up_interruptible(x) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程, 只唤醒其中的一个线程 |
wake_up_interruptible_nr(x, nr) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程, 只唤醒其中的 nr 个线程 |
wake_up_interruptible_all(x) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程, 唤醒其中的所有线程 |
wake_up(x) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”或 “TASK_UNINTERRUPTIBLE”的线程,只唤醒其中的一个线程 |
wake_up_nr(x, nr) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”或 “TASK_UNINTERRUPTIBLE”的线程,只唤醒其中 nr个线程 |
wake_up_all(x) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”或 “TASK_UNINTERRUPTIBLE”的线程,唤醒其中的所有线程 |
3. 使用休眠与唤醒的驱动框架
下面是通过按键来使用“休眠 - 唤醒”,框架如下:
要休眠的线程,放在 wq 队列里,中断处理函数从 wq 队列里把它取出来唤醒。所以,需要做以下几件事:
- 初始化 wq 队列
- 在驱动的 read 函数中,调用
wait_event_interruptible
:它本身会判断 event 是否为 FALSE,如果为 FASLE 表示无数据,则休眠。当从wait_event_interruptible
返回后,把数据复制回用户空间。 - 在中断服务程序里:设置 event 为 TRUE,并调用 wake_up_interruptible 唤醒线程。
4. 编写测试程序
在驱动测试程序中,要先定义“wait queue”:
static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
在驱动的读函数里调用wait_event_interruptible
:
ssize_t gpio_key_read(struct file *pFile, char __user *pBuf, size_t size, loff_t *ppos)
ssize_t len;
int err;
wait_event_interruptible(gpio_key_wait, key_event_flag);
len = sizeof(struct app_key_event);
err = copy_to_user(pBuf, &appKeyPayload, len);
key_event_flag = false;
return len;
在wait_event_interruptible(gpio_key_wait, key_event_flag);
中,不一定会进入休眠,它会先判断key_event_flag
是否为TRUE,不为TRUE那么就进入休眠。我们可以通过按下按键后,进入中断服务程序后,设置为key_event_flag
为TRUE,然后唤醒当前任务。
static irqreturn_t gpio_key_isr(int irq, void *dev_id)
int val;
struct gpio_key_cfg *pGpioKey = dev_id;
val = gpiod_get_value(pGpioKey->gpiod);
printk("key %d %d\\n", pGpioKey->gpio_num, val);
appKeyPayload.gpio_num = pGpioKey->gpio_num;
appKeyPayload.value = val;
key_event_flag = true;
wake_up_interruptible(&gpio_key_wait);
return IRQ_HANDLED;
下面是应用测试程序,在while中不断的read获取key值并打印,具体代码如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
/*
* ./app_key_test /dev/gpio_key0
*
*/
struct app_key_event
int gpio_num;
int value;
;
int main(int argc, char *argvp[])
int fd;
struct app_key_event keyEventData;
/* 1.判断参数 */
if(argc != 2)
printf("Usage: %s <dev>\\n", argvp[0]);
return -1;
/* 2.打开文件 */
fd = open(argvp[1], O_RDWR);
if(fd == -1)
printf("can not open file %s\\n", argvp[1]);
return -1;
/* 3.读取KEY Event */
while(1)
read(fd, &keyEventData, sizeof(struct app_key_event));
printf("get key event: <%d><%d>\\n", keyEventData.gpio_num, keyEventData.value);
return 0;
;
5. 验证测试
编译烧录后,验证测试结果如下:
6. 完整工程代码
测试验证的完整工程代码下载地址如下:
正在上传中…
以上是关于Linux内核中休眠与唤醒的使用(wait_eventwake_up)的主要内容,如果未能解决你的问题,请参考以下文章
Linux内核中休眠与唤醒的使用(wait_eventwake_up)