文章目录
“休眠-唤醒”机制:
当应用程序必须等待某个事件发生,比如必须等待按键被按下时,可以使用“休眠-唤醒”机制:
- ① APP 调用 read 等函数试图读取数据,比如读取按键;
- ② APP 进入内核态,也就是调用驱动中的对应函数,发现有数据则复制到用户空间并马上返回;
- ③ 如果 APP 在内核态,也就是在驱动程序中发现没有数据,则
APP 休眠; - ④ 当有数据时,比如当按下按键时,
驱动程序的中断服务程序被调用,它会记录数据、唤醒 APP; - ⑤ APP 继续运行它的内核态代码,也就是驱动程序中的函数,复制数据到用户空间并马上返回。
APP执行过程

- 驱动中没有数据时,APP1 在内核态
执行到 drv_read 时会休眠。 - 所谓休眠就是把自己的状态改为
非 RUNNING,这样内核的调度器就不会让它运行。 - 当按下按键,驱动程序中的中断服务程序被调用,它会记录数据,并唤醒 APP1。所以唤醒就是把程序的状态改为
RUNNING,这样内核的调度器有合适的时间就会让它运行。 - 当 APP1 再次运行时,就会
继续执行 drv_read 中剩下的代码,把数据复制回用户空间,返回用户空间。 - 在 APP的read到内核态的drv_read函数中(进程上下文),也就是在 APP1 的执行过程中,它是可以休眠的
- 在中断处理函数中(属于中断上下文),不能休眠,也就是不能调用会导致休眠的函数。
内核调度器负责维护该链表,链表里面保存的是线程,如果线程的状态为RUNNING,则会找到合适的时间就会让它运行,如果是非RUNNING,内核的调度器就不会让它运行。
内核函数
参考内核源码: 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 为真,信号也不好使 |
比较重要的参数就是:
- ① wq:waitqueue,等待队列
- 休眠时除了把程序状态改为非 RUNNING 之外,还要把进程/进程放入wq 中,以后中断服务程序要从 wq 中把它取出来唤醒。
- 没有 wq 的话,茫茫人海中,中断服务程序去哪里找到你?
- ② condition
- 这可以是一个变量,也可以是任何表达式。表示“一直等待,直到condition 为真”
唤醒函数
| 函数 | 说明 |
|---|---|
| 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”的线程,唤醒其中的所有线程 |
休眠与唤醒方式的按键驱动程序(stm32mp157)
驱动程序框架

要休眠的线程,放在 wq 队列里,中断处理函数从 wq 队列里把它取出来唤醒。
代码编写内容
- ① 初始化 wq 队列
- ②
在驱动的 read 函数中,调用 wait_event_interruptible:- 它本身会判断 event 是否为 FALSE,如果为 FASLE 表示无数据,则休眠。当从 wait_event_interruptible 返回后,把数据复制回用户空间。
- ③
在中断服务程序里:- 设置 event 为 TRUE,并调用 wake_up_interruptible 唤醒线程。
button_test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
/*
* ./button_test /dev/100ask_button0
*
*/
int main(int argc, char **argv)
{
int fd;
int val;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
while (1)
{
/* 3. 读文件 */
read(fd, &val, 4);
printf("get button : 0x%x\n", val);
}
close(fd);
return 0;
}
gpio_key_drv.c
使用环形缓冲区来保存按键值,相比于全局变量,可以避免被覆盖的问题
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include

文章详细阐述了在嵌入式系统中,如何使用“休眠-唤醒”机制来优化APP对按键输入的处理。通过内核函数和中断服务程序,当APP尝试读取按键数据但无数据时,会进入休眠状态,而当按键按下触发中断时,中断服务程序会唤醒APP,从而高效地处理事件。文中还给出了STM32MP157平台的按键驱动程序的框架和具体实现步骤。
&spm=1001.2101.3001.5002&articleId=131891564&d=1&t=3&u=fc0cccb3541d4c95b40cb3ddea33c510)
2762

被折叠的 条评论
为什么被折叠?



