kworkers_work为什么名词

kworkers_work为什么名词1.简介:    在spi驱动中用到了内核的线程,用的函数就是跟kthread_worker和kthread_work相关的函数,对于这两个名词的翻译,在网上暂时没有找到合适的,先翻译成线程内核线程相关的:工人和工作,这么直白的翻译是根据其工作原理相关的,本来想翻译成别的,一想到他的实现方式,直白的翻译,更能让人理解。    此部分介绍的函数主要在inc

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

1. 简介:

        在spi驱动中用到了内核的线程,用的函数就是跟 kthread_worker 和 kthread_work 相关的函数,对于这两个名词的翻译,在网上暂时没有找到合适的,先翻译成线程内核线程相关的:工人和工作,这么直白的翻译是根据其工作原理相关的,本来想翻译成别的,一想到他的实现方式,直白的翻译,更能让人理解。

        此部分介绍的函数主要在 include/linux/kthread.h 文件,这里可以推测,也许是内核为了方便我们使用内核的线程,而设计的kthread_work和kthread_worker。

2. 函数:

2.1 先来看这两个结构体:


kthread_worke

kthread_worker

[cpp] 
view plain  
copy

 
print
?

  1. struct kthread_worker {  
  2.     spinlock_t      lock;  
  3.     struct list_head    work_list;  
  4.     struct task_struct  *task;  
  5.     struct kthread_work *current_work;  
  6. };  
  7.   
  8. struct kthread_work {  
  9.     struct list_head    node;  
  10.     kthread_work_func_t func;  
  11.     wait_queue_head_t   done; //  等待队列,内部成员是一个锁和一个链表节点  
  12.     struct kthread_worker   *worker;  
  13. };  

【1】其中的 wait_queue_head_t 结构体需要解析一下:

[cpp] 
view plain  
copy

 
print
?

  1. struct __wait_queue_head { // 是一个带锁的链表节点  
  2.     spinlock_t lock;  
  3.     struct list_head task_list;  
  4. };  
  5. typedef struct __wait_queue_head wait_queue_head_t;  


2.2 声明:


DEFINE_KTHREAD_WORK宏

DEFINE_KTHREAD_WORKER宏

[cpp] 
view plain  
copy

 
print
?

  1. #define KTHREAD_WORKER_INIT(worker) {               \  
  2.     .lock = __SPIN_LOCK_UNLOCKED((worker).lock),            \ // 初始化worker中lock  
  3.     .work_list = LIST_HEAD_INIT((worker).work_list),        \ // 初始化worker中的链表节点 work_list  
  4.     }  
  5.   
  6. #define KTHREAD_WORK_INIT(work, fn) {               \  
  7.     .node = LIST_HEAD_INIT((work).node),                \ // 初始化work中的链表节点 node (next和pre指针指向自己的首地址)  
  8.     .func = (fn),                           \ // func成员赋值  
  9.     .done = __WAIT_QUEUE_HEAD_INITIALIZER((work).done),     \ // 初始化 done 成员 (初始化等待队列中的锁和链表节点,  
  10.     }                                                                 // 链表节点的初始化就是next和pre指针指向节点的首地址)  
  11.   
  12. #define __WAIT_QUEUE_HEAD_INITIALIZER(name) {               \  
  13.     .lock       = __SPIN_LOCK_UNLOCKED(name.lock),      \  
  14.     .task_list  = { &(name).task_list, &(name).task_list } }  
  15.   
  16. #define DEFINE_KTHREAD_WORKER(worker)                   \  
  17.     struct kthread_worker worker = KTHREAD_WORKER_INIT(worker)  
  18.   
  19. #define DEFINE_KTHREAD_WORK(work, fn)                   \  
  20.     struct kthread_work work = KTHREAD_WORK_INIT(work, fn)  


2.3 初始化


init_kthread_work宏

init_kthread_worker宏

[cpp] 
view plain  
copy

 
print
?

  1. #define init_kthread_worker(worker)                 \ // 初始化 kthread_worker  
  2.     do {                                \  
  3.         static struct lock_class_key __key;         \  
  4.         __init_kthread_worker((worker), “(“#worker“)->lock”, &__key); \  
  5.     } while (0)  
  6.   
  7. #define init_kthread_work(work, fn)                 \ // 初始化 kthread_work  
  8.     do {                                \  
  9.         memset((work), 0, sizeof(struct kthread_work));     \  
  10.         INIT_LIST_HEAD(&(work)->node);               \ // 初始化成员 node 链表节点  
  11.         (work)->func = (fn);                 \ // 将回调函数的指针指向fn函数,内核线程将会一直执行的函数  
  12.         init_waitqueue_head(&(work)->done);          \ // 初始化成员 done (等待队列)  
  13.     } while (0)  
  14.   
  15. void __init_kthread_worker(struct kthread_worker *worker,  
  16.                 const char *name,  
  17.                 struct lock_class_key *key)  
  18. {  
  19.     spin_lock_init(&worker->lock);  
  20.     lockdep_set_class_and_name(&worker->lock, key, name); // 跟防止死锁有关,此处不深究  
  21.     INIT_LIST_HEAD(&worker->work_list); // 初始化 work_list 链表节点  
  22.     worker->task = NULL;  
  23. }  

2.4 内核线程一直执行的函数


kthread_worker_fn函数

[cpp] 
view plain  
copy

 
print
?

  1. /** 
  2.  * kthread_worker_fn – kthread 函数目的是执行 kthread_worker中work_list下的work,此函数是作为内核线程中一直执行的函数 
  3.  * @worker_ptr: 指向初始化了的 kthread_worker 
  4.  */  
  5. int kthread_worker_fn(void *worker_ptr)  
  6. {  
  7.     struct kthread_worker *worker = worker_ptr;  
  8.     struct kthread_work *work;  
  9.   
  10.     WARN_ON(worker->task);  
  11.     worker->task = current;  
  12. repeat:  
  13.     set_current_state(TASK_INTERRUPTIBLE);  /* mb paired w/ kthread_stop */  
  14.   
  15.     if (kthread_should_stop()) { // 如果接收到线程停止的信号  
  16.         __set_current_state(TASK_RUNNING);  
  17.         spin_lock_irq(&worker->lock);  
  18.         worker->task = NULL;  
  19.         spin_unlock_irq(&worker->lock);  
  20.         return 0;  
  21.     }  
  22.   
  23.     work = NULL;  
  24.     spin_lock_irq(&worker->lock);  
  25.     if (!list_empty(&worker->work_list)) { // 如果 worker中的work_list链表不是空的  
  26.         work = list_first_entry(&worker->work_list, // 取出头结点后边的第一个结构体kthread_work  
  27.                     struct kthread_work, node);  
  28.         list_del_init(&work->node); // 删除链表中的入口  
  29.     }  
  30.     worker->current_work = work; // 将正在处理的work地址赋给 worker中的current_work成员  
  31.     spin_unlock_irq(&worker->lock);  
  32.   
  33.     if (work) { // 如果有 work  
  34.         __set_current_state(TASK_RUNNING); // 启动内核线程  
  35.         work->func(work); // 运行work中的func函数  
  36.     } else if (!freezing(current)) // 如果没有work要做,并且没有freeze,则主动请求调度,主动放弃cpu时间片  
  37.         schedule();  
  38.   
  39.     try_to_freeze();  
  40.     goto repeat; // 无限循环  
  41. }  

【1】可以看到,这个函数的关键就是重复的执行kthread_worker结构体中的work_list链表锁挂接的kthread_work中的func函数,直到work_list变为空为止。

【2】要知道的是kthread是内核线程,是一直运行在内核态的线程

【3】这个函数一般是作为回调函数使用,比如spi.c中的如下程序

[cpp] 
view plain  
copy

 
print
?

  1. master->kworker_task = kthread_run(kthread_worker_fn,  
  2.                        &master->kworker,  
  3.                        dev_name(&master->dev));  
  4.   
  5. /** 
  6.  * kthread_run – 创建并唤醒一个内核线程 
  7.  * @threadfn: the function to run until signal_pending(current). 
  8.  * @data: data ptr for @threadfn. 
  9.  * @namefmt: printf-style name for the thread. 
  10.  * 
  11.  * Description: Convenient wrapper for kthread_create() followed by 
  12.  * wake_up_process().  Returns the kthread or ERR_PTR(-ENOMEM). 
  13.  */  
  14. #define kthread_run(threadfn, data, namefmt, …) …(此处省略)  

2.5 队列化kthread_work


queue_kthread_work 函数

[cpp] 
view plain  
copy

 
print
?

  1. /** 
  2.  * queue_kthread_work – 队列化一个 kthread_work,实质是将work中的node节点挂接到worker中的work_list后边,并尝试唤醒worker中的任务 
  3.  * @worker: 目标 kthread_worker 
  4.  * @work: 要队列化的 kthread_work 
  5.  * 
  6.  * 队列化 @work 目的是为了让任务异步执行.  @task 
  7.  * 必须已经被 kthread_worker_create() 创建了.   
  8.  * 队列化成功,返回true,不成功返回false 
  9.  */  
  10. bool queue_kthread_work(struct kthread_worker *worker,  
  11.             struct kthread_work *work)  
  12. {  
  13.     bool ret = false;  
  14.     unsigned long flags;  
  15.   
  16.     spin_lock_irqsave(&worker->lock, flags);  
  17.     if (list_empty(&work->node)) {   // 这里保证要插入到worker中链表节点的work的node节点一定要是一个独立的,不能是一串  
  18.         insert_kthread_work(worker, work, &worker->work_list);  
  19.         ret = true;  
  20.     }  
  21.     spin_unlock_irqrestore(&worker->lock, flags);  
  22.     return ret;  
  23. }  
  24.   
  25. /* 在@worker中的work_list链表中的@pos位置的后边插入@work中的链表节点 */  
  26. static void insert_kthread_work(struct kthread_worker *worker,  
  27.                    struct kthread_work *work,  
  28.                    struct list_head *pos)  
  29. {  
  30.     lockdep_assert_held(&worker->lock);  
  31.   
  32.     list_add_tail(&work->node, pos);  
  33.     work->worker = worker; // work中的worker指针指向worker  
  34.     if (likely(worker->task))  
  35.         wake_up_process(worker->task); // 尝试唤醒一下 worker 中的task指向的线程来处理work  
  36. }  


2.6 执行完worker中的work


flush_kthread_worker 函数

[cpp] 
view plain  
copy

 
print
?

  1. struct kthread_flush_work {  
  2.     struct kthread_work work;  
  3.     struct completion   done;  
  4. };  
  5.   
  6. static void kthread_flush_work_fn(struct kthread_work *work)  
  7. {  
  8.     struct kthread_flush_work *fwork =  
  9.         container_of(work, struct kthread_flush_work, work);  
  10.     complete(&fwork->done); // 唤醒完成量  
  11. }  
  12.   
  13. /** 
  14.  * flush_kthread_worker – flush all current works on a kthread_worker 
  15.  * @worker: worker to flush 
  16.  * 
  17.  * Wait until all currently executing or pending works on @worker are 
  18.  * finished. 
  19.  */  
  20. void flush_kthread_worker(struct kthread_worker *worker)  
  21. {  
  22.     struct kthread_flush_work fwork = {  
  23.         KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn),  
  24.         COMPLETION_INITIALIZER_ONSTACK(fwork.done), // ON_STACK后缀相当于加了static  
  25.     };  
  26.   
  27.     queue_kthread_work(worker, &fwork.work); // 将 fwork中的work成员的node节点过接到worker_list下,并尝试唤醒线程进行kthread_flush_work_fn函数的执行  
  28.     wait_for_completion(&fwork.done); // 调用这个函数的线程睡眠等待在这里,等待执行worker中work_list下的fulsh_kthread_work完kthread_flush_work_fn函数  
  29. }  

【1】如何就flush了呢?看2.7的总结

2.7 总结的一张图:


        说了半天,其实woker和work的关系还是很难理解的,当我们经过一段时间,再次看的时候,难免还要花很长时间,因此,我画了一张图:

kworkers_work为什么名词

【1】worker中的task执行的是各自work中的func指定的函数,此规则同样适用于kthread_flush_work

【2】kthread_flush_work中的函数是kthread.c文件指定的函数,而kthread_work中的函数是用户自己定义的函数

【3】每次唤醒线程执行的work都是worker中的work_list下挂载的正常顺序的第一个

【4】如何实现等待全部的work都执行完呢?调用的是flush_kthread_worker函数中的wait_for_completion(&fwork.done);语句,只有当前边的work都执行完,才能轮到kthread_flush_work中的kthread_flush_work_fn的执行,此函数就是唤醒kthread_flush_work中的done。从而确定了前边的work都执行完了


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/189765.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • SVN安装与使用_刚安装ETC不能马上使用吗

    SVN安装与使用_刚安装ETC不能马上使用吗SVN(Subversion)是近年来崛起的版本管理工具,在当前的开源项目里(J2EE),几乎95%以上的项目都用到了SVN。Subversion项目的初衷是为了替换当年开源社区最为流行的版本控制软件CVS,在CVS的功能的基础上有很多的提升同时也能较好的解决CVS系统的一些不足。svn是基于客户/服务器模式,所以分客户端和服务器端,作为开发人员,自己的电脑上只需要安装客户端,又…

  • 5.6(java学习笔记) queue

    5.6(java学习笔记) queue

  • 什么是纠删码_脑疝的常见类型

    什么是纠删码_脑疝的常见类型你能给纠删码一个好的定义吗? EthanMiller:纠删码是在丢失部分数据的情况下根据剩余数据将丢失的数据重建的一组算法。举个例子,如果我想保护六份数据,我会使用一种纠删码算法来产生两份额外的数据,这样总共就会有八份数据。这八份数据中的任意六份数据都能恢复另外两份数据。纠删码的要点是你可以选择对数据做任意数量的分片。我知道一些纠删码可以将数据至多分成200片或者奇数片,你也可以选择校验数

    2022年10月25日
  • 体验云计算的成果—-亚马逊免费云计算服务

    体验云计算的成果—-亚马逊免费云计算服务

  • sublime 4 202107 激活码(注册激活)

    (sublime 4 202107 激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html…

  • LaTeX学习:Texlive 2019和TeX studio的安装及使用「建议收藏」

    LaTeX学习:Texlive 2019和TeX studio的安装及使用「建议收藏」文章目录1.LaTex介绍2.Texlive2019的下载和安装(1)下载(2)安装3.TeXstudio的安装以及简单使用(1)设置中文界面(2)添加行号(3)设置编译器与编码(4)第一个简单程序4.扩展1.LaTex介绍LaTeX基于TeX,主要目的是为了方便排版。在学术界的论文,尤其是数学、计算机等学科论文都是由LaTeX编写,因为用它写数学公式非常漂亮。…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号