Driver porting: the workqueue interface.

工作队列机制
本文介绍Linux内核2.6中引入的工作队列机制,作为旧任务队列接口的替代方案。工作队列为每个CPU提供专用的工作线程,允许任务在进程上下文中运行并能够睡眠。文章还详细介绍了如何创建和使用工作队列,包括初始化、调度和取消任务的方法。

转自:http://lwn.net/Articles/23634/

Driver porting: the workqueue interface.

This article is part of the LWN Porting Drivers to 2.6 series.
The longstanding task queue interface was removed in 2.5.41; in its place is a new "workqueue" mechanism. Workqueues are very similar to task queues, but there are some important differences. Among other things, each workqueue has one or more dedicated worker threads (one per CPU, by default) associated with it. So all tasks running out of workqueues have a process context, and can thus sleep. Note that access to user space is not possible from code running out of a workqueue; there simply is no user space to access. Drivers can create their own work queues - with their own worker threads - but there is a default queue (for each processor) provided by the kernel that will work in most situations.

Workqueues are created with one of:

    struct workqueue_struct *create_workqueue(const char *name);
    struct workqueue_struct *create_singlethread_workqueue(const char *name);
A workqueue created with  create_workqueue() will have one worker thread for each CPU on the system;  create_singlethread_workqueue(), instead, creates a workqueue with a single worker process. The name of the queue is limited to ten characters; it is only used for generating the "command" for the kernel thread(s) (which can be seen in  ps or  top).

Tasks to be run out of a workqueue need to be packaged in a struct work_struct structure. This structure may be declared and initialized at compile time as follows:

    DECLARE_WORK(name, void (*function)(void *), void *data);
Here,  name is the name of the resulting  work_struct structure,  function is the function to call to execute the work, and  data is a pointer to pass to that function.

To set up a work_struct structure at run time, instead, use the following two macros:

    INIT_WORK(struct work_struct *work, 
              void (*function)(void *), void *data);
    PREPARE_WORK(struct work_struct *work, 
                 void (*function)(void *), void *data);
The difference between the two is that  INIT_WORK initializes the linked list pointers within the  work_struct structure, while  PREPARE_WORK changes only the function and data pointers. INIT_WORK must be used at least once before queueing the  work_struct structure, but should  not be used if the  work_struct might already be in a workqueue.

Actually queueing a job to be executed is simple:

    int queue_work(struct workqueue_struct *queue, 
                   struct work_struct *work);
    int queue_delayed_work(struct workqueue_struct *queue, 
	                   struct work_struct *work,
                           unsigned long delay);
The second form of the call ensures that a minimum delay (in jiffies) passes before the work is actually executed. The return value from both functions is nonzero if the  work_struct was actually added to  queue (otherwise, it may have already been there and will not be added a second time).

Entries in workqueues are executed at some undefined time in the future, when the associated worker thread is scheduled to run (and after the delay period, if any, has passed). If it is necessary to cancel a delayed task, you can do so with:

    int cancel_delayed_work(struct work_struct *work);
Note that this workqueue entry could actually be executing when  cancel_delayed_work() returns; all this function will do is keep it from starting after the call.

To ensure that none of your workqueue entries are running, call:

    void flush_workqueue(struct workqueue_struct *queue);
This would be a good thing to do, for example, in a device driver shutdown routine. Note that if the queue contains work with long delays this call could take a long time to complete. This function will  not (as of 2.5.68) wait for any work entries submitted after the call was first made; you should ensure that, for example, any outstanding work queue entries will not resubmit themselves. You should also cancel any delayed entries (with  cancel_delayed_work()) first if need be.

Work queues can be destroyed with:

    void destroy_workqueue(struct workqueue_struct *queue);
This operation will flush the queue, then delete it.

Finally, for tasks that do not justify their own workqueue, a "default" work queue (called "events") is defined. work_struct structures can be added to this queue with:

    int schedule_work(struct work_struct *work);
    int schedule_delayed_work(struct work_struct *work, unsigned long delay);
Most users of workqueues can probably use the predefined queue, but one should bear in mind that it is a shared resource. Long delays in the worker function will slow down other users of the queue, and should be avoided. There is a  flush_scheduled_work() function which will wait for everything on this queue to be executed. If your module uses the default queue, it should almost certainly call  flush_scheduled_work() before allowing itself to be unloaded.

One final note: schedule_work()schedule_delayed_work() and flush_scheduled_work() are exported to any modules which wish to use them. The other functions (for working with separate workqueues) are exported to GPL-licensed modules only.


( Log in to post comments)

Driver porting: the workqueue interface.

Posted Jun 17, 2003 21:45 UTC (Tue) by btl (guest, #12097) [Link]

I'm trying to playing with workqueues... i've trouble on compiling
a little kernel module.

What include files do i need beside linux/workqueue.h?

I can't find where struct workqueue_struct is defined ;(

Driver porting: the workqueue interface.

Posted Dec 9, 2003 19:17 UTC (Tue) by bighead (guest, #17582) [Link]

Its work_struct, not workqueue_struct (if I get what you are asking).

Second you might need the usual, linux/init.h, linux/module.h etc header files. Take a look at http://www.xml.com/ldd/chapter/book/. The book is mainly 2.4 based. You can then see http://lwn.net/Articles/driver-porting/ for differences in 2.4 and 2.6.

Cheers!
Archit

Correction

Posted Apr 22, 2004 16:29 UTC (Thu) by proski (subscriber, #104) [Link]

Among other things, each workqueue has one or more dedicated worker threads (one per CPU) associated with it. So all tasks running out of workqueues have a process context, and can thus sleep.
This incorrectly implies that the task queue in 2.4 kernels is not run in the process context and cannot sleep. It's not true. The task queue is run in the context of the "keventd" process.

I believe the only significant difference is that the workqueue is processed in per-CPU threads whereas task queue is processed by a single kernel thread.

Correction

Posted Apr 22, 2004 16:41 UTC (Thu) by corbet (editor, #1) [Link]

Actually, 2.4 had several task queues, most of which did not run in process context. The scheduler queue, in later 2.4 kernels, runs out of keventd, but it is a single, shared thread. Workqueues have multiple, per-CPU threads which are dedicated to the queue. (When Rusty's patch goes in, single-thread workqueues will also be possible, though each queue will still have its own thread).

Workqueues also have nice features like a "flush" operation that can guarantee that your tasks are not running and will not run and a "delayed work" capability.

Driver porting: the workqueue interface.

Posted Aug 16, 2004 1:16 UTC (Mon) by baisa (guest, #24024) [Link]

How do I schedule work tasks (if indeed this is the object I should be using) to execute every timer tick? In the book, in chap 6 "Flow of Time" it lists at least 3 kernel task queues, one of which was a timer queue that dispatched its tasks each tick. But the entire "linux/tqueue.h" functionality was deleted in 2.6, and now I can't figure out how to dispatch some code every timer tick.

Thanks! Brad

Driver porting: the workqueue interface.

Posted Sep 15, 2004 7:05 UTC (Wed) by Albert (guest, #24733) [Link]

I ran into the same problem. We ran a stepper motor from the timer interrupt. In 2.6 I used the RTC instead. Here is some code for a minimal module that uses RTC to generate IRQ8 at 1024 Hz and also registers an interrupt handler for IRQ8 (via the RTC module).

/* 
* stepper.c - Stepper motor driver using RTC.
*/
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_ALERT */
#include <linux/init.h> /* Needed for the macros */
#include <linux/ioctl.h>

#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE)

#include <linux/rtc.h>

#define DRIVER_AUTHOR "John Doe <some@one>"
#define DRIVER_DESC "Stepper motor driver using RTC"

static rtc_task_t rtc_task;
static atomic_t rtc_inc = ATOMIC_INIT(0);
static int stepper_freq = 1024; /* Frequency Hz */

static void stepper_interrupt(void *private_data)
{
int ticks;

atomic_inc(&rtc_inc);
ticks = atomic_read(&rtc_inc);

/* Add some code here to drive the stepper motor */
if (ticks % stepper_freq == 0) {
printk(KERN_WARNING "stepper_interrupt: %d\n", ticks);
}
}

static int __init stepper_init(void)
{
int err;
printk(KERN_ALERT "stepper_init: registering task\n");

rtc_task.func = stepper_interrupt;
err = rtc_register(&rtc_task);
if (err < 0)
return err;
rtc_control(&rtc_task, RTC_IRQP_SET, stepper_freq);
rtc_control(&rtc_task, RTC_PIE_ON, 0);
atomic_set(&rtc_inc, 0);
return 0;
}

static void __exit stepper_exit(void)
{
rtc_control(&rtc_task, RTC_PIE_OFF, 0);
rtc_unregister(&rtc_task);
printk(KERN_ALERT "stepper_exit: rtc_task unregistered\n");
}

module_init(stepper_init);
module_exit(stepper_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */

Driver porting: the workqueue interface.

Posted Jan 20, 2005 6:17 UTC (Thu) by amit2030 (guest, #27378) [Link]

Hi,

I wanted to know whether the workqueue gets scheduled before a kernel thread (created by kthread_run()). I have a function that runs in the thread created by kthread_run(). If I use the workqueue interface, then will it be scheduled before other threads created by kthread_run().

Regards,

Driver porting: the workqueue interface.

Posted May 16, 2005 0:44 UTC (Mon) by Freiberg (guest, #29960) [Link]

Could you please tell me more about Work Queue synchronization?
Are they cumulative? If I scheduled the same work several times inside
one isr handler or different isr handlers will all of them run or not and in which sequence?

Thanks,
Vlad

Driver porting: the workqueue interface.

Posted Sep 3, 2008 21:43 UTC (Wed) by peetee3000 (guest, #53726) [Link]

Note that the workqueue interface has changed slightly in v2.6.20:

http://www.linuxhq.com/kernel/v2.6/20/include/linux/workq...

Ie, INIT_WORK now takes only 2 parameters.


基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究”展开,提出了一种结合数据驱动方法与Koopman算子理论的递归神经网络(RNN)模型线性化方法,旨在提升纳米定位系统的预测控制精度与动态响应能力。研究通过构建数据驱动的线性化模型,克服了传统非线性系统建模复杂、计算开销大的问题,并在Matlab平台上实现了完整的算法仿真与验证,展示了该方法在高精度定位控制中的有效性与实用性。; 适合人群:具备一定自动化、控制理论或机器学习背景的科研人员与工程技术人员,尤其是从事精密定位、智能控制、非线性系统建模与预测控制相关领域的研究生与研究人员。; 使用场景及目标:①应用于纳米级精密定位系统(如原子力显微镜、半导体制造设备)中的高性能预测控制;②为复杂非线性系统的数据驱动建模与线性化提供新思路;③结合深度学习与经典控制理论,推动智能控制算法的实际落地。; 阅读建议:建议读者结合Matlab代码实现部分,深入理解Koopman算子与RNN结合的建模范式,重点关注数据预处理、模型训练与控制系统集成等关键环节,并可通过替换实际系统数据进行迁移验证,以掌握该方法的核心思想与工程应用技巧。
基于粒子群算法优化Kmeans聚类的居民用电行为分析研究(Matlb代码实现)内容概要:本文围绕基于粒子群算法(PSO)优化Kmeans聚类的居民用电行为分析展开研究,提出了一种结合智能优化算法与传统聚类方法的技术路径。通过使用粒子群算法优化Kmeans聚类的初始聚类中心,有效克服了传统Kmeans算法易陷入局部最优、对初始值敏感的问题,提升了聚类的稳定性和准确性。研究利用Matlab实现了该算法,并应用于居民用电数据的行为模式识别与分类,有助于精细化电力需求管理、用户画像构建及个性化用电服务设计。文档还提及相关应用场景如负荷预测、电力系统优化等,并提供了配套代码资源。; 适合人群:具备一定Matlab编程基础,从事电力系统、智能优化算法、数据分析等相关领域的研究人员或工程技术人员,尤其适合研究生及科研人员。; 使用场景及目标:①用于居民用电行为的高效聚类分析,挖掘典型用电模式;②提升Kmeans聚类算法的性能,避免局部最优问题;③为电力公司开展需求响应、负荷预测和用户分群管理提供技术支持;④作为智能优化算法与机器学习结合应用的教学与科研案例。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,深入理解PSO优化Kmeans的核心机制,关注参数设置对聚类效果的影响,并尝试将其应用于其他相似的数据聚类问题中,以加深理解和拓展应用能力。
edk2-porting 是一个专注于将 EDK II(Extensible Firmware Interface Development Kit II)移植到不同硬件平台的开源项目。EDK II 是由 TianoCore 组织开发的一套用于构建 UEFI(Unified Extensible Firmware Interface)固件的工具和库集合。该项目的目标是为各种芯片组和主板提供通用的固件支持,从而促进固件开发的标准化和模块化。 ### 项目特点 - **模块化架构**:EDK II 采用模块化设计,允许开发者通过组合不同的模块来构建固件映像。这种设计使得固件开发更加灵活,并且可以适应不同的硬件需求。 - **跨平台支持**:edk2-porting 项目致力于将 EDK II 移植到多种硬件平台上,包括但不限于 x86、ARM 和 RISC-V 架构[^1]。 - **开源社区驱动**:该项目由开源社区维护,吸引了来自全球的开发者参与。社区提供了丰富的文档和工具,帮助开发者快速上手并贡献代码。 ### 如何访问 edk2-porting 项目页面 1. **GitHub 页面**: edk2-porting 项目的官方 GitHub 页面可以通过以下步骤访问: - 打开浏览器,访问 [GitHub](https://github.com)。 - 在搜索栏中输入 `edk2-porting`,然后按下回车键。 - 在搜索结果中找到由 `edk2-porting` 组织或相关仓库名称匹配的项目页面。 2. **直接链接**: 如果你知道具体的仓库名称,可以直接通过以下格式访问: ``` https://github.com/edk2-porting/<repository-name> ``` 其中 `<repository-name>` 是你要访问的具体仓库名称。 3. **文档和资源**: - **README 文件**:每个仓库通常会包含一个 `README.md` 文件,详细介绍了项目的背景、安装步骤、构建指南以及如何贡献代码。 - **Wiki 页面**:部分仓库可能还提供了 Wiki 页面,包含更详细的文档和常见问题解答。 ### 示例代码 以下是一个简单的 Python 脚本,用于检查 GitHub 上是否存在名为 `edk2-porting` 的组织: ```python import requests def check_github_organization(org_name): url = f"https://api.github.com/orgs/{org_name}" response = requests.get(url) if response.status_code == 200: print(f"Organization '{org_name}' exists on GitHub.") else: print(f"Organization '{org_name}' does not exist on GitHub.") check_github_organization("edk2-porting") ``` ### 相关问题 1. 如何在 Windows 上构建 EDK II 项目? 2. edk2-porting 项目支持哪些硬件平台? 3. 如何为 edk2-porting 项目贡献代码? 4. EDK II 与 UEFI 之间有什么区别? 5. 如何调试 EDK II 固件?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YasinLeeX

再来一杯西湖龙井。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值