Linux : task work 机制

task work机制可以在内核中向指定的进程添加一些任务函数,这些任务函数会在进程返回用户态时执行,使用的是该进程的上下文。包括下面的这些API:

  • task_work_add
  • task_work_cancel
  • task_work_run

进程对象task_struct中有个字段用来存储这些待进行的任务列表头即task_works,这个结构体包含一个next指针和需要执行的函数指针。

205 /**
206  * struct callback_head - callback structure for use with RCU and task_work
207  * @next: next update requests in a list
208  * @func: actual update function to call after the grace period.
209  */
210 struct callback_head {
211         struct callback_head *next;
212         void (*func)(struct callback_head *head);
213 };
  4
  5 static struct callback_head work_exited; /* all we need is ->next == NULL */
  6
  7 /**
  8  * task_work_add - ask the @task to execute @work->func()
  9  * @task: the task which should run the callback
 10  * @work: the callback to run
 11  * @notify: send the notification if true
 12  *
 13  * Queue @work for task_work_run() below and notify the @task if @notify.
 14  * Fails if the @task is exiting/exited and thus it can‘t process this @work.
 15  * Otherwise @work->func() will be called when the @task returns from kernel
 16  * mode or exits.
 17  *
 18  * This is like the signal handler which runs in kernel mode, but it doesn‘t
 19  * try to wake up the @task.
 20  *
 21  * RETURNS:
 22  * 0 if succeeds or -ESRCH.
 23  */
 24 int
 25 task_work_add(struct task_struct *task, struct callback_head *work, bool notify)
 26 {
 27         struct callback_head *head;
 28
 29         do {
 30                 head = ACCESS_ONCE(task->task_works);
 31                 if (unlikely(head == &work_exited))
 32                         return -ESRCH;
 33                 work->next = head;
 34         } while (cmpxchg(&task->task_works, head, work) != head);
 35
 36         if (notify)
 37                 set_notify_resume(task);
 38         return 0;
 39 }

主要工作:

1. 通过CAS以无锁的形式添加了一个链表元素。(新元素排在原有链表头部)

2. set_notify_resume函数向指定的进程设置了一个_TIF_NOTIFY_RESUME标记。

task_work_run执行时机

在返回用户态之前会对当前进程的标记检查,如果相关标记置位则会调用do_notify_resume

595 int_signal:
596         testl $_TIF_DO_NOTIFY_MASK,%edx
597         jz 1f
598         movq %rsp,%rdi          # &ptregs -> arg1
599         xorl %esi,%esi          # oldset -> arg2
600         call do_notify_resume
601 1:      movl $_TIF_WORK_MASK,%edi
602 int_restore_rest:
603         RESTORE_REST
604         DISABLE_INTERRUPTS(CLBR_NONE)
605         TRACE_IRQS_OFF
606         jmp int_with_check
607         CFI_ENDPROC
608 END(system_call)

以上文件为entry_64.S,而标记定义在thread_info.c中

130 /* work to do on interrupt/exception return */
131 #define _TIF_WORK_MASK                                                  132         (0x0000FFFF &                                                   133          ~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|                       134            _TIF_SINGLESTEP|_TIF_SECCOMP|_TIF_SYSCALL_EMU))
 70 #define TIF_SYSCALL_TRACE       0       /* syscall trace active */
 71 #define TIF_NOTIFY_RESUME       1       /* callback before returning to user */
 72 #define TIF_SIGPENDING          2       /* signal pending */
 73 #define TIF_NEED_RESCHED        3       /* rescheduling necessary */
 74 #define TIF_SINGLESTEP          4       /* reenable singlestep on user return*/
 75 #define TIF_SYSCALL_EMU         6       /* syscall emulation active */
 76 #define TIF_SYSCALL_AUDIT       7       /* syscall auditing active */
 77 #define TIF_SECCOMP             8       /* secure computing */
 78 #define TIF_MCE_NOTIFY          10      /* notify userspace of an MCE */
 79 #define TIF_USER_RETURN_NOTIFY  11      /* notify kernel of userspace return */
 80 #define TIF_UPROBE              12      /* breakpointed or singlestepping */
 81 #define TIF_NOTSC               16      /* TSC is not accessible in userland */
 82 #define TIF_IA32                17      /* IA32 compatibility process */
 83 #define TIF_FORK                18      /* ret_from_fork */
 84 #define TIF_NOHZ                19      /* in adaptive nohz mode */
 85 #define TIF_MEMDIE              20      /* is terminating due to OOM killer */
 86 #define TIF_POLLING_NRFLAG      21      /* idle is polling for TIF_NEED_RESCHED */
 87 #define TIF_IO_BITMAP           22      /* uses I/O bitmap */
 88 #define TIF_FORCED_TF           24      /* true if TF in eflags artificially */
 89 #define TIF_BLOCKSTEP           25      /* set when we want DEBUGCTLMSR_BTF */
 90 #define TIF_LAZY_MMU_UPDATES    27      /* task is updating the mmu lazily */
 91 #define TIF_SYSCALL_TRACEPOINT  28      /* syscall tracepoint instrumentation */
 92 #define TIF_ADDR32              29      /* 32-bit address space on 64 bits */
 93 #define TIF_X32                 30      /* 32-bit native x86-64 binary */
 94 

即_TIF_WORK_MASK表示除开(_TIF_SYSCALL_TRACE, _TIF_SYSCALL_AUDIT, _TIF_SINGLESTEP, _TIF_SECCOMP, _TIF_SYSCALL_EMU)之外的所有标记。自然包括了_TIF_NOTIFY_RESUME标记。

do_notify_resume函数

729 /*
730  * notification of userspace execution resumption
731  * - triggered by the TIF_WORK_MASK flags
732  */
733 __visible void
734 do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
735 {
736         user_exit();
737
738 #ifdef CONFIG_X86_MCE
739         /* notify userspace of pending MCEs */
740         if (thread_info_flags & _TIF_MCE_NOTIFY)
741                 mce_notify_process();
742 #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
743
744         if (thread_info_flags & _TIF_UPROBE)
745                 uprobe_notify_resume(regs);
746
747         /* deal with pending signal delivery */
748         if (thread_info_flags & _TIF_SIGPENDING)
749                 do_signal(regs);
750
751         if (thread_info_flags & _TIF_NOTIFY_RESUME) {
752                 clear_thread_flag(TIF_NOTIFY_RESUME);
753                 tracehook_notify_resume(regs);
754         }
755         if (thread_info_flags & _TIF_USER_RETURN_NOTIFY)
756                 fire_user_return_notifiers();
757
758         user_enter();
759 }

可以看到在其中调用tracehook_notify_resume函数,也包括其他一些如信号处理相关的函数。

tracehook_notify_resume

174 /**
175  * tracehook_notify_resume - report when about to return to user mode
176  * @regs:               user-mode registers of @current task
177  *
178  * This is called when %TIF_NOTIFY_RESUME has been set.  Now we are
179  * about to return to user mode, and the user state in @regs can be
180  * inspected or adjusted.  The caller in arch code has cleared
181  * %TIF_NOTIFY_RESUME before the call.  If the flag gets set again
182  * asynchronously, this will be called again before we return to
183  * user mode.
184  *
185  * Called without locks.
186  */
187 static inline void tracehook_notify_resume(struct pt_regs *regs)
188 {
189         /*
190          * The caller just cleared TIF_NOTIFY_RESUME. This barrier
191          * pairs with task_work_add()->set_notify_resume() after
192          * hlist_add_head(task->task_works);
193          */
194         smp_mb__after_atomic();
195         if (unlikely(current->task_works))
196                 task_work_run();
197 }

在进程对象的task_works不为null的情况下才有任务需要执行。

task_work_run

 77 /**
 78  * task_work_run - execute the works added by task_work_add()
 79  *
 80  * Flush the pending works. Should be used by the core kernel code.
 81  * Called before the task returns to the user-mode or stops, or when
 82  * it exits. In the latter case task_work_add() can no longer add the
 83  * new work after task_work_run() returns.
 84  */
 85 void task_work_run(void)
 86 {
 87         struct task_struct *task = current;
 88         struct callback_head *work, *head, *next;
 89
 90         for (;;) {
 91                 /*
 92                  * work->func() can do task_work_add(), do not set
 93                  * work_exited unless the list is empty.
 94                  */
 95                 do {
 96                         work = ACCESS_ONCE(task->task_works);
 97                         head = !work && (task->flags & PF_EXITING) ?
 98                                 &work_exited : NULL;
 99                 } while (cmpxchg(&task->task_works, work, head) != work);
100
101                 if (!work)
102                         break;
103                 /*
104                  * Synchronize with task_work_cancel(). It can‘t remove
105                  * the first entry == work, cmpxchg(task_works) should
106                  * fail, but it can play with *work and other entries.
107                  */
108                 raw_spin_unlock_wait(&task->pi_lock);
109                 smp_mb();
110
111                 /* Reverse the list to run the works in fifo order */
112                 head = NULL;
113                 do {
114                         next = work->next;
115                         work->next = head;
116                         head = work;
117                         work = next;
118                 } while (work);
119
120                 work = head;
121                 do {
122                         next = work->next;
123                         work->func(work);
124                         work = next;
125                         cond_resched();
126                 } while (work);
127         }
128 }

1. 通过CAS,以无锁的方式取得task_works链表

2. 因为原链表是按元素添加到链表的时间逆序排列的(见task_work_add),先把链表反转一遍

3. 反转链表后,遍历链表,执行各个元素的任务函数即work->func(work)

时间: 2025-01-13 11:11:48

Linux : task work 机制的相关文章

Linux内核同步机制

http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环境来提高操作系统效率.首先,看看我们最熟悉的两种机制——信号量.锁. 一.信号量 首先还是看看内核中是怎么实现的,内核中用struct semaphore数据结构表示信号量(<linux/semphone.h>中): [cpp] view plaincopyprint? struct semaph

Linux内核同步机制--转发自蜗窝科技

Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个位于memory中的变量的值到寄存器中 2.修改该变量的值(也就是修改寄存器中的值) 3.将寄存器中的数值写回memory中的变量值 如果这个操作序列是串行化的操作(在一个thread中串行执行),那么一切OK,然而,世界总是不能如你所愿.在多CPU体系结构中,运行在两个CPU上的两个内核控制路径同

Linux内核抢占机制 - 实现

本文首发于 http://oliveryang.net,转载时请包含原文或者作者网站链接. 本文主要围绕 Linux 内核调度器 Preemption 的相关实现进行讨论.其中涉及的一般操作系统和 x86 处理器和硬件概念,可能也适用于其它操作系统. 1. Scheduler Overview Linux 调度器的实现实际上主要做了两部分事情, 任务上下文切换 在 Preemption Overview 里,我们对任务上下文切换做了简单介绍.可以看到,任务上下文切换有两个层次的实现:公共层和处理

Linux内存管理机制

一.首先大概了解一下计算机CPU.Cache.内存.硬盘之间的关系及区别. 1.  CPU也称为中央处理器(CPU,Central Processing Unit)是一块超大规模的集成电路, 是一台计算机的运算核心(Core)和控制核心( Control Unit).它的功能主要是解释计算机指令以及处理计算机软件中的数据.中央处理器主要由三核心部件组成,运算器.控制器和总线(BUS),运算器又主要由算术逻辑单元(ALU)和寄存器(RS)组成. 2.Cache即高速缓冲存储器,是位于CPU与主内存

[内核同步]浅析Linux内核同步机制

转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral 很早之前就接触过同步这个概念了,但是一直都很模糊,没有深入地学习了解过,近期有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这两本书的相关章节.趁刚看完,就把相关的内容总结一下.为了弄清楚什么事同步机制,必须要弄明白以下三个问题: 什么是互

linux的一些机制Signal, Fork,

signal(SIGCHLD, SignalHandler); 注册软中断,对应的api close(socket); ret=fork(): 父进程,返回子进程的pid. 子进程返回0, 出错返回<0 set_process_name("Name"), 当前进程名称 dpopen, dlclose, dlsym linux的一些机制Signal, Fork,,布布扣,bubuko.com

linux各种IPC机制(进程通信)

linux各种IPC机制 (2011-07-08 16:58:35)     原文地址:linux各种IPC机制(转)作者:jianpengliu 原帖发表在IBM的developerworks网站上,是一个系列的文章,作者郑彦兴,通过讲解和例子演示了Linux中几种IPC的使用方式,我觉得很好,在这里做一个保留,能看完的话Linux IPC的基础是没有问题的了.一)Linux环境进程间通信(一)管道及有名管道http://www.ibm.com/developerworks/cn/linux/

浅谈Linux内存管理机制

经常遇到一些刚接触Linux的新手会问内存占用怎么那么多? 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这方面,区别于Windows的内存管理.主要特点是,无论物理内存有多大,Linux 都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能.而Windows是只在需要内存时,才为应用程序分配内存,并不能充分利用大容量的内存空间.换句话说,每增加一

linux runtime pm机制的深入理解

一:runtime机制说明 何为runtime机制?也就是系统在非睡眠状态,设备在空闲时可以进入runtime suspend状态同时不依赖系统wake_lock机制,非空闲时执行runtime resume使得设备进入正常工作状态. 主要代码放在Runtime.c (drivers\base\power)中,同时附带的Runtime_pm.txt (documentation\power)有详细 说明.要使得设备可以进入runtime_idle与runtime_suspend必须满足devic