[前记]Linux自从2.6.20之后,工作队列发生了一些变化,目前从网络上搜索的资料一般都是介绍老版本的工作队列,很少见到对新版本的介绍。本文对新老版本都做了简要概述,并分别提供了简单的实作案例。
*******************************************************************************************************************
工作队列(work queue)是Linux kernel中将工作推后执行的一种机制。这种机制和BH或Tasklets不同之处在于工作队列是把推后的工作交由一个内核线程去执行,因此工作队列的优势就在于它允许重新调度甚至睡眠。
工作队列是2.6内核开始引入的机制,在2.6.20之后,工作队列的数据结构发生了一些变化,因此本文分成两个部分对2.6.20之前和之后的版本分别做介绍。
1、2.6.0~2.6.19
数据结构:
struct work_struct {
unsigned long pending;
struct list_head entry;
void (*func)(void *);
void *data;
void *wq_data;
struct timer_list timer;
};
pending是用来记录工作是否已经挂在队列上;
entry是循环链表结构;
func作为函数指针,由用户实现;
data用来存储用户的私人数据,此数据即是func的参数;
wq_data一般用来指向工作者线程(工作者线程参考下文);
timer是推后执行的定时器。
work_struct的这些变量里,func和data是用户使用的,其他是内部变量,我们可以不用太过关心。
API:
1) INIT_WORK(_work, _func, _data)
初始化指定工作,目的是把用户指定的函数_func及_func需要的参数_data赋给work_struct的func及data变量。
2) int schedule_work(struct work_struct *work)
对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。工作者线程本质上是一个普通的内核线程,在默认情况下,每个CPU均有一个类型为“events”的工作者线程,当调用schedule_work时,这个工作者线程会被唤醒去执行工作链表上的所有工作。
3) int schedule_delayed_work(struct work_struct *work, unsigned long delay)
延迟执行工作,与schedule_work类似。
4) void flush_scheduled_work(void)
刷新缺省工作队列。此函数会一直等待,直到队列中的所有工作都被执行。
5) int cancel_delayed_work(struct work_struct *work)
flush_scheduled_work并不取消任何延迟执行的工作,因此,如果要取消延迟工作,应该调用cancel_delayed_work。
以上均是采用缺省工作者线程来实现工作队列,其优点是简单易用,缺点是如果缺省工作队列负载太重,执行效率会很低,这就需要我们创建自己的工作者线程和工作队列。
API:
1) struct workqueue_struct *create_workqueue(const char *name)
创建新的工作队列和相应的工作者线程,name用于该内核线程的命名。
2) int queue_work(struct workqueue_struct *wq, struct work_struct *work)
类似于schedule_work,区别在于queue_work把给定工作提交给创建的工作队列wq而不是缺省队列。
3) int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay)
延迟执行工作。
4) void flush_workqueue(struct workqueue_struct *wq)
刷新指定工作队列。
5) void destroy_workqueue(struct workqueue_struct *wq)
释放创建的工作队列。
下面一段代码可以看作一个简单的实作:
void my_func(void *data)
{
char *name = (char *)data;
printk(KERN_INFO “Hello world, my name is %s!\n”, name);
}
struct workqueue_struct *my_wq = create_workqueue(“my wq”);
struct work_struct my_work;
INIT_WORK(&my_work, my_func, “Jack”);
queue_work(my_wq, &my_work);
destroy_workqueue(my_wq);
2、2.6.20~2.6.??
自2.6.20起,工作队列的数据结构发生了一些变化,使用时不能沿用旧的方法。
数据结构:
typedef void (*work_func_t)(struct work_struct *work);
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
};
与2.6.19之前的版本相比,work_struct瘦身不少。粗粗一看,entry和之前的版本相同,func和data发生了变化,另外并无其他的变量。
entry我们不去过问,这个和以前的版本完全相同。data的类型是atomic_long_t,这个类型从字面上看可以知道是一个原子类型。第一次看到这个变量时,很容易误认为和以前的data是同样的用法,只不过类型变了而已,其实不然,这里的data是之前版本的pending和wq_data的复合体,起到了以前的pending和wq_data的作用。
func的参数是一个work_struct指针,指向的数据就是定义func的work_struct。
看到这里,会有两个疑问,第一,如何把用户的数据作为参数传递给func呢?以前有void *data来作为参数,现在好像完全没有办法做到;第二,如何实现延迟工作?目前版本的work_struct并没有定义timer。
解决第一个问题,需要换一种思路。2.6.20版本之后使用工作队列需要把work_struct定义在用户的数据结构中,然后通过container_of来得到用户数据。具体用法可以参考稍后的实作。
对于第二个问题,新的工作队列把timer拿掉的用意是使得work_struct更加单纯。首先回忆一下之前版本,只有在需要延迟执行工作时才会用到timer,普通情况下timer是没有意义的,所以之前的做法在一定程度上有些浪费资源。所以新版本中,将timer从work_struct中拿掉,然后又定义了一个新的结构delayed_work用于处理延迟执行:
struct delayed_work {
struct work_struct work;
struct timer_list timer;
};
下面把API罗列一下,每个函数的解释可参考之前版本的介绍或者之后的实作:
1) INIT_WORK(struct work_struct *work, work_func_t func)
2) INIT_DELAYED_WORK(struct delayed_work *work, work_func_t func)
3) int schedule_work(struct work_struct *work)
4) int schedule_delayed_work(struct delayed_work *work, unsigned long delay)
5) struct workqueue_struct *create_workqueue(const char *name)
6) int queue_work(struct workqueue_struct *wq, struct work_struct *work)
7) int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay)
8) void flush_scheduled_work(void)
9) void flush_workqueue(struct workqueue_struct *wq)
10) int cancel_delayed_work(struct delayed_work *work)
11) void destroy_workqueue(struct workqueue_struct *wq)
其中,1), 2), 4) ,7)和以前略有区别,其他用法完全一样。
实作:
struct my_struct_t {
char *name;
struct work_struct my_work;
};
void my_func(struct work_struct *work)
{
struct my_struct_t *my_name = container_of(work, struct my_struct_t, my_work);
printk(KERN_INFO “Hello world, my name is %s!\n”, my_name->name);
}
struct workqueue_struct *my_wq = create_workqueue(“my wq”);
struct my_struct_t my_name;
my_name.name = “Jack”;
INIT_WORK(&(my_name.my_work), my_func);
queue_work(my_wq, &(my_name.my_work));
destroy_workqueue(my_wq);
相关推荐
下面我们将深入探讨队列的数据结构、工作原理以及在Linux中的应用。 队列是一种线性数据结构,其特点是数据元素按照先进先出(First In First Out, FIFO)的规则进行操作。在Linux中,队列通常用于缓存数据、任务...
发送端读取指定的文件,并且按照环境变量中设置的消息队列键值进行发送。如果要改代码,只要把键值改一下,结构体储存要发送的消息的那个数组对应改成自己想发送的值,就可以很好的实现功能。接收端同样按环境变量...
Linux操作系统内核中的工作队列(workqueue)是一种机制,用于在后台线程中异步处理非紧急任务,从而避免阻塞系统响应。工作队列是内核调度的一部分,它允许可延迟操作在适当的时间点由工作队列的处理线程执行。下面...
- **`thread`**:指向负责执行工作队列中工作的线程的任务结构指针。 - **`run_depth`**:检测`run_workqueue()`函数的递归深度,防止死锁。 另外,`struct workqueue_struct`代表了一个逻辑上的工作队列,它包含了...
很多博客都有深入分析内核队列的工作原理,但是少有能够拿来直接运行的demo,这个demo亲测可用!
要将等待队列 entry 添加到等待队列中,需要使用 prepare_to_wait 函数,void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);该函数将等待队列 entry 添加到等待队列中,并设置进程的...
2. **研究消息的发送和接收**:通过msgsnd和msgrcv函数,了解消息如何被添加到队列、如何从队列中移除以及如何处理满队列或空队列的情况。 3. **分析msgctl操作**:查看如何改变消息队列的属性、删除队列以及进行...
这些消息在队列中按照先进先出(FIFO)的顺序排列,接收进程可以从队列中取出并处理消息。与管道和套接字等其他IPC机制相比,消息队列的一个显著优点是其非同步特性,发送进程不必等待接收进程立即处理消息,可以...
- **__kfifo_put**: 向队列中添加数据。如果队列已满,则根据当前可用空间返回实际可写入的数据量。 - **__kfifo_get**: 从队列中取出数据。如果队列为空,则返回实际可读取的数据量。 #### 并发与同步 `kfifo`的...
然后,它使用 `fgets` 函数从标准输入读取用户输入,并将其发送到消息队列中。同时,它还使用 `msgrcv` 函数接收从 Peter 发送的消息,并将其打印到屏幕上。 在 msgPeter.c 程序中,首先创建了一个消息队列,并将其...
此外,这些工作队列中的任务执行是严格串行的,意味着任务之间的并行处理能力有限,这在多处理器系统中降低了整体效率。 针对这些问题,Linux内核2.6.36引入了受控并发工作队列机制。这个新机制允许内核动态地根据...
拥有写权限的进程可以根据一定规则向队列添加新消息,而具有读权限的进程可以从队列中读取消息。新消息通常被添加到队列的末尾,而读取时不一定从队列头部开始,可以从中部开始读取。消息队列与进程关联但不随进程...
6. **队列遍历**:遍历队列中的所有元素,这通常用于调试或打印队列内容。 ```c void printQueue(Queue* q) { Node* temp = q->front; while (temp != NULL) { printf("%d ", temp->data); temp = temp->next;...
只有拥有写权限的进程可以向消息队列中添加新消息,而只有拥有读权限的进程可以从消息队列中读出消息。 查看系统中的消息队列可以使用 ipcs 命令,删除消息队列可以使用 ipcrm 命令。 下面是一个使用消息队列的...
1. `ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)`: 从消息队列中接收消息。`msgtyp`用于指定要接收的消息类型,`msgsz`是缓冲区的大小,`msgflg`包含接收选项,如阻塞或非阻塞。...
消息队列中的消息通常包含一个类型标识,使得接收进程可以选择接收特定类型的消息。 三、消息队列的使用步骤 1. 创建消息队列:使用msgget创建一个新的消息队列,或者打开已存在的消息队列。该调用需要指定队列键...
Linux中的工作队列(新版本)-进程管理- Linux中的工作队列(新版本)-进程管理-
通过学习这些案例,开发者可以深入理解消息队列的工作原理以及如何在实际项目中应用。 描述中提到“Linux环境下编写消息队列”,这可能包括创建、发送、接收消息以及管理和删除消息队列的过程。经典案例通常会涵盖...
Linux消息队列是一种在进程间通信(IPC)中使用的机制,它允许进程向队列中写入消息,然后由其他进程读取。Linux系统继承了System V的IPC机制,其中包括消息队列、信号量和共享内存。消息队列是线性数据结构,每个...
在Linux操作系统中,进程间通信(IPC,Inter-Process Communication)是多个进程共享数据或交换信息的一种机制。本文将深入探讨如何利用消息队列这一IPC机制实现进程间的双向通信。消息队列允许进程异步地发送和接收...