文章目录
“工作队列”机制:
定时器、下半部 tasklet,它们都是在中断上下文中执行,它们无法休眠。当要处理更复杂的事情时,往往更耗时。这些更耗时的工作放在定时器或是下半部中,会使得系统很卡;并且循环等待某件事情完成也太浪费CPU 资源了。如果使用线程来处理这些耗时的工作,那就可以解决系统卡顿的问题:因为线程可以休眠。
所以:工作队列的应用场合:要做的事情比较耗时,甚至可能需要休眠,那么可以使用工作队列。
在内核中,我们并不需要自己去创建线程,可以使用“工作队列”(workqueue)。内核初始化工作队列是,就为它创建了内核线程。以后我们要使用“工作队列”,只需要把“工作”放入“工作队列中”,对应的内核线程就会取出“工作”,执行里面的函数。
缺点:多个工作(函数)是在某个内核线程中依序执行的,前面函数执行很慢,就会影响到后面的函数【解决方法是单独使用一个线程而不是使用系统默认的队列】。但是在多 CPU的系统下,一个工作队列可以有多个内核线程,可以在一定程度上缓解这个问题。
内核函数
参考内核头文件:include\linux\workqueue.h
work_struct 结构体
内核线程、工作队列(workqueue)都由内核创建了
核心是一个 work_struct 结构体

定义 work
#define DECLARE_WORK(n, f) \
struct work_struct n = __WORK_INITIALIZER(n, f)
#define DECLARE_DELAYED_WORK(n, f) \
struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, 0)
- 第 1 个宏是用来定义一个
work_struct结构体,要指定它的函数。 - 第 2 个宏用来定义一个 delayed_work 结构体,也要指定它的函数。所以“delayed”,意思就是说要让它运行时,可以指定:某段时间之后你再执行。
如果要在代码中初始化 work_struct 结构体,可以使用下面的宏:
#define INIT_WORK(_work, _func)
使用 work :schedule_work
调用 schedule_work 时,就会把 work_struct 结构体放入队列中,并唤醒对应的内核线程(自动)。内核线程就会从队列里把 work_struct 结构体取出来,执行里面的函数。
workqueue 其他函数
| 函数 | 说明 |
|---|---|
| create_workqueue | 在 Linux 系统中已经有了现成的 system_wq 等工作队列,你当然也可以自己调用 create_workqueue 创建工作队列,对于 SMP 系统,这个工作队列会有多个内核线程与它对应,创建工作队列时,内核会帮这个工作队列创建多个内核线程 |
| create_singlethread_workqueue | 如果想只有一个内核线程与工作队列对应,可以用本函数创建工作队列,创建工作队列时,内核会帮这个工作队列创建一个内核线程 |
| destroy_workqueue | 销毁工作队列 |
| schedule_work | 调度执行一个具体的 work,执行的 work 将会被挂入 Linux 系统提供的工作队列 |
| schedule_delayed_work | 延迟一定时间去执行一个具体的任务,功能与 schedule_work 类似,多了一个延迟时间 |
| queue_work | 跟 schedule_work 类似,schedule_work 是在系统默认的工作队列上执行一个work,queue_work 需要自己指定工作队列 |
| queue_delayed_work | 跟 schedule_delayed_work 类似,schedule_delayed_work 是在系统默认的工作队列上执行一个 work,queue_delayed_work 需要自己指定工作队列 |
| flush_work | 等待一个 work 执行完毕,如果这个 work 已经被放入队列,那么本函数等它执行完毕,并且返回 true;如果这个 work 已经执行完华才调用本函数,那么直接返回false |
| flush_delayed_work | 等待一个 delayed_work 执行完毕,如果这个 delayed_work 已经被放入队列,那么本函数等它执行完毕,并且返回 true;如果这个 delayed_work 已经执行完华才调用本函数,那么直接返回 false |
工作队列方式的按键驱动程序(stm32mp157)
编程思路
使用工作队列时,步骤如下:
- 第1步 构造一个 work_struct 结构体,里面有函数;
- 第2步 把这个 work_struct 结构体放入工作队列,内核线程就会运行 work 中的函数。
button_test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
static int fd;
/*
* ./button_test /dev/100ask_button0
*
*/
int main(int argc, char **argv)
{
int val;
struct pollfd fds[1];
int timeout_ms = 5000;
int ret;
int flags;
int i;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR | O_NONBLOCK);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
for (i = 0; i < 10; i++)
{
if (read(fd, &val, 4) == 4)
printf("get button: 0x%x\n", val);
else
printf("get button: -1\n");
}
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
while (1)
{
if (read(fd, &val, 4) == 4)
printf("get button: 0x%x\n", val);
else
printf("while get button: -1\n");
}
close(fd);
return 0;
}
gpio_key_drv.c
#include <linux/module.h>
#

文章介绍了Linux内核中的工作队列机制,用于处理耗时且可能需要休眠的任务,避免系统卡顿。工作队列通过内核线程执行工作,包括结构体定义、函数调度以及相关API。同时,文章给出了基于STM32MP157芯片的工作队列按键驱动程序的编程思路,包括driver的实现和设备树的修改。
&spm=1001.2101.3001.5002&articleId=131920476&d=1&t=3&u=4ed69e38fe944e698f6bd5098a3f1a50)
1117

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



