linux 工作队列(workqueue)——非常详细易懂

2024-02-05 08:58

本文主要是介绍linux 工作队列(workqueue)——非常详细易懂,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.csdn.net/liuxd3000/article/details/7700247

在处理内核相关工作中, 我们经常看到工作队列(workqueue)的身影. 本文描述何为 linux workqueue.本文基于 2.6.32 的内核, 此时的工作队列还不是 cmwq.

为什么使用 workqueue?

在内核代码中, 经常希望延缓部分工作到将来某个时间执行, 这样做的原因很多, 比如

.在持有锁时做大量(或者说费时的)工作不合适.
.希望将工作聚集以获取批处理的性能.
.调用了一个可能导致睡眠的函数使得在此时执行新调度非常不合适.
...
内核中提供了许多机制来提供延迟执行, 使用最多则是 workqueue.

.如中断的下半部处理可延迟中断上下文中的部分工作;
.定时器可指定延迟一定时间后执行某工作;
.workqueue 则允许在进程上下文环境下延迟执行;
.内核中曾短暂出现过的慢工作机制 (slow work mechanism);
.异步函数调用(asynchronous function calls);
.各种私有实现的线程池;
...
术语

workqueue: 所有工作项(需要被执行的工作)被排列于该队列.
worker thread: 是一个用于执行 workqueue 中各个工作项的内核线程, 当 workqueue 中没有工作项时, 该线程将变为 idle 状态.
single threaded(ST): worker thread 的表现形式之一, 在系统范围内, 只有一个 worker thread 为 workqueue 服务.
multi threaded(MT): worker thread 的表现形式之一, 在多 CPU 系统上每个 CPU 上都有一个 worker thread 为 workqueue 服务.
使用步骤


创建 workqueue(如果使用内核默认的 workqueue, 此步骤略过).
创建工作项 work_struct.
向 workqueue 提交工作项.
工作项


数据结构(略有调整):


struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;
    struct lockdep_map lockdep_map;
};
struct delayed_work {
    struct work_struct work;
    struct timer_list timer;
};
静态地创建工作项:

DECLARE_WORK(n, f)
DECLARE_DELAYED_WORK(n, f)
动态地创建工作项:

INIT_WORK(struct work_struct work, work_func_t func); 
PREPARE_WORK(struct work_struct work, work_func_t func); 
INIT_DELAYED_WORK(struct delayed_work work, work_func_t func); 
PREPARE_DELAYED_WORK(struct delayed_work work, work_func_t func); 
内核默认的 workqueue

查阅源码可知, 内核默认的全局 workqueue 应为:

// 定义
static struct workqueue_struct *keventd_wq __read_mostly;


// 初始化
...
keventd_wq = create_workqueue("events"); // MT worker thread 模式.
...


// 确认对应的内核线程数目, 应等于 CPU 核数
$ lscpu
Architecture:          x86_64
CPU(s):                3
Thread(s) per core:    1
$ ps aux | grep "events"
root         9  0.0  0.0      0     0 ?        S    Feb09   1:15 [events/0]
root        10  0.0  0.0      0     0 ?        S    Feb09   0:59 [events/1]
root        11  0.0  0.0      0     0 ?        S    Feb09   0:59 [events/2]
工作项加入 keventd_wq 由下面两个函数之一完成:


int schedule_work(struct work_struct *work)
{
    return queue_work(keventd_wq, work);
}
int schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)
{
    return queue_delayed_work(keventd_wq, dwork, delay);
}
用户自定义的 workqueue

创建 workqueue:

create_singlethread_workqueue(name) // 仅对应一个内核线程
create_workqueue(name) // 对应多个内核线程, 同上文.
向 workqueue 中提交工作项:

int queue_work(workqueue_t *queue, work_t *work); 
int queue_delayed_work(workqueue_t *queue, work_t *work, unsigned long delay); 
取消 workqueue 中挂起的工作项以及释放 workqueue 此处略过.

工作队列的优点

使用简单.
执行在进程上下文, 从而可以睡眠, 被调度和抢占.
在多核环境下使用也非常友好.
example

#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>

static struct workqueue_struct *queue = NULL;
static struct work_struct work;

static void work_handler(struct work_struct *data)
{
    printk(KERN_ALERT "work handler for work_item in queue helloworkqueue\n");
    // workqueue 中的每个工作完成之后就被移除 workqueue.
    // 下面的语句会造成"死机", 原因可能是该 workqueue 占据了所有的 CPU 时间.
    // queue_work(queue, &work);
}

static int __init test_init(void)
{
    queue = create_singlethread_workqueue("helloworkqueue");
    if (!queue)
    {   
        goto err;
    }   
    INIT_WORK(&work, work_handler);
    queue_work(queue, &work);

    return 0;
err:
    return -1; 
}
static void __exit test_exit(void)
{
    destroy_workqueue(queue);
}

MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);
view rawworkqueue_example.cThis Gist brought to you by GitHub.
Makefile:

obj-m := wq.o
default:
    make -C /usr/src/linux-2.6.32.12-0.7 M=`pwd` modules


这篇关于linux 工作队列(workqueue)——非常详细易懂的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/680395

相关文章

Java集合中的List超详细讲解

《Java集合中的List超详细讲解》本文详细介绍了Java集合框架中的List接口,包括其在集合中的位置、继承体系、常用操作和代码示例,以及不同实现类(如ArrayList、LinkedList和V... 目录一,List的继承体系二,List的常用操作及代码示例1,创建List实例2,增加元素3,访问元

Linux使用cut进行文本提取的操作方法

《Linux使用cut进行文本提取的操作方法》Linux中的cut命令是一个命令行实用程序,用于从文件或标准输入中提取文本行的部分,本文给大家介绍了Linux使用cut进行文本提取的操作方法,文中有详... 目录简介基础语法常用选项范围选择示例用法-f:字段选择-d:分隔符-c:字符选择-b:字节选择--c

SpringBoot整合easy-es的详细过程

《SpringBoot整合easy-es的详细过程》本文介绍了EasyES,一个基于Elasticsearch的ORM框架,旨在简化开发流程并提高效率,EasyES支持SpringBoot框架,并提供... 目录一、easy-es简介二、实现基于Spring Boot框架的应用程序代码1.添加相关依赖2.添

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多