`
gqf2008
  • 浏览: 76782 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

内核中工作队列(linux工作队列)

阅读更多

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_work);
destroy_workqueue(my_wq);

分享到:
评论

相关推荐

    Linux操作系统内核中工作队列的操作

    - **`thread`**:指向负责执行工作队列中工作的线程的任务结构指针。 - **`run_depth`**:检测`run_workqueue()`函数的递归深度,防止死锁。 另外,`struct workqueue_struct`代表了一个逻辑上的工作队列,它包含了...

    Linux操作系统内核工作队列的操作模式

    Linux操作系统内核中的工作队列(workqueue)是一种机制,用于在后台线程中异步处理非紧急任务,从而避免阻塞系统响应。工作队列是内核调度的一部分,它允许可延迟操作在适当的时间点由工作队列的处理线程执行。下面...

    Linux内核新旧工作队列机制的剖析和比较.pdf

    此外,这些工作队列中的任务执行是严格串行的,意味着任务之间的并行处理能力有限,这在多处理器系统中降低了整体效率。 针对这些问题,Linux内核2.6.36引入了受控并发工作队列机制。这个新机制允许内核动态地根据...

    linux内核工作队列demo

    很多博客都有深入分析内核队列的工作原理,但是少有能够拿来直接运行的demo,这个demo亲测可用!

    一种基于内核定时器和工作队列的Linux rootkit.pdf

    一种基于内核定时器和工作队列的Linux rootkit.pdf

    Linux内核中的无锁队列 - kfifo

    - **__kfifo_put**: 向队列中添加数据。如果队列已满,则根据当前可用空间返回实际可写入的数据量。 - **__kfifo_get**: 从队列中取出数据。如果队列为空,则返回实际可读取的数据量。 #### 并发与同步 `kfifo`的...

    \Linux内核机制之等待队列

    当资源变得可用时,内核会唤醒等待队列中的某些进程。唤醒的过程会根据不同的情况采取不同的策略。例如,如果资源具有排他性,那么只会唤醒设置了`WQ_FLAG_EXCLUSIVE`标志的进程中的一个。 #### 五、总结 通过...

    linux中的队列源代码

    下面我们将深入探讨队列的数据结构、工作原理以及在Linux中的应用。 队列是一种线性数据结构,其特点是数据元素按照先进先出(First In First Out, FIFO)的规则进行操作。在Linux中,队列通常用于缓存数据、任务...

    内核等待队列机制介绍

    在使用 wait queue 时,需要首先创建一个等待队列,然后将进程添加到等待队列中。当某个事件发生时,可以使用 wake_up 函数来唤醒等待的进程。 例如,下面的代码演示了如何使用 wait queue 实现设备驱动程序中的...

    linux 内核精髓-精通linux内核必会的75个绝技

    1. **Linux内核概述**:首先,书中会介绍Linux内核的基本架构,包括内核的模块化设计、进程管理、内存管理和I/O处理机制等,这些都是理解内核工作流程的基础。 2. **进程管理**:深入讲解进程的创建、调度、同步和...

    Linux内核的等待队列[汇编].pdf

    等待队列的使用还包括将进程添加到队列中(add_wait_queue())、从队列中移除(remove_wait_queue())、以及唤醒等待的进程(wake_up())等操作。这些操作都需要在适当的锁保护下进行,以防止并发问题。 总的来说,...

    LINUX内核经典面试题

    1. Linux内核锁:Linux内核中主要有自旋锁和信号量两种锁机制。自旋锁用于保护短暂的、不会引起阻塞的临界区,而信号量则允许任务在无法获取锁时进入睡眠状态,适合处理可能长时间持有锁的情况。 2. 用户模式与内核...

    linux消息队列源码

    2. **研究消息的发送和接收**:通过msgsnd和msgrcv函数,了解消息如何被添加到队列、如何从队列中移除以及如何处理满队列或空队列的情况。 3. **分析msgctl操作**:查看如何改变消息队列的属性、删除队列以及进行...

    Linux内核设计的艺术

    进程间通信(IPC)机制允许不同进程之间共享数据和协调工作,内核提供了多种IPC机制,如信号、管道、消息队列、共享内存和套接字等。 6. 网络协议栈:Linux内核的网络协议栈支持从链路层到应用层的多种网络协议。...

    嵌入式Linux内核中的等待队列操作.pdf

    在嵌入式Linux内核中,等待队列是实现进程同步和通信的关键机制,它允许进程在等待某个特定事件发生时进入休眠状态,而当该事件发生时,被唤醒继续执行。等待队列的操作涉及到了进程调度、睡眠与唤醒以及内核的并发...

    深入linux设备驱动程序内核机制

    书中不仅关注内核模块的构建,还涉及驱动程序与内核交互的关键技术,如工作队列、锁机制等。 内核模块是Linux系统中实现设备驱动程序的一种方式,它们是可动态加载和卸载的代码片段。在第一章中,作者详细介绍了...

    中断服务下半部之工作队列详解

    4. **执行环境(Execution Context)**:工作队列中的任务会在一个称为工作者线程(Worker Thread)的内核线程上下文中执行。这意味着工作函数可以调用用户态函数,使用锁和等待队列,甚至睡眠。 5. **同步机制...

    Linux网络体系结构 Linux内核中网络协议的设计与实现

    通过阅读《Linux网络体系结构:Linux内核中网络协议的设计与实现》,读者可以深入理解Linux网络的工作原理,这对于系统管理员、开发人员和研究人员来说是不可或缺的知识。书中详细解释了内核源码中的关键部分,并...

    Linux消息队列分析及应用

    拥有写权限的进程可以根据一定规则向队列添加新消息,而具有读权限的进程可以从队列中读取消息。新消息通常被添加到队列的末尾,而读取时不一定从队列头部开始,可以从中部开始读取。消息队列与进程关联但不随进程...

    Linux内核设计与实现(第三版中文高清带目录)_linux_linux内核_

    以上这些内容都是《Linux内核设计与实现》第三版可能涵盖的主题,读者可以通过这本书深入了解Linux内核的工作原理,提高对操作系统底层机制的理解。对于系统管理员、软件开发者和计算机科学的学生来说,这是一本极其...

Global site tag (gtag.js) - Google Analytics