Linux内核机制—task_work
基于msm-5.4
一、task_work简介
task_work实现一种延迟执行机制,将回调延迟到返回用户空间时执行。是内核固定存在的功能,不依赖于任何开关控制宏。相关实现文件:
kernel/task_work.c
include/linux/task_work.h
二、task_work相关数据结构
此模块比较简单,并没有特有的相关数据结构,使用的是系统共用的。
1. struct callback_head
struct callback_head { //linux/types.h struct callback_head *next; void (*func)(struct callback_head *head); }
2. struct task_struct
struct task_struct { struct callback_head task_works; }
task_work的添加、遍历、移除,操作的都是这个成员。
三、task_work相关接口
本机制实现比较简单,没有特有的数据结构,这里列出其所有的接口:
//include/linux/task_work.h typedef void (*task_work_func_t)(struct callback_head *); static inline void init_task_work(struct callback_head *twork, task_work_func_t func) { twork->func = func; } int task_work_add(struct task_struct *task, struct callback_head *twork, bool); struct callback_head *task_work_cancel(struct task_struct *, task_work_func_t); void task_work_run(void); static inline void exit_task_work(struct task_struct *task) { task_work_run(); }
1. task_work_add
将初始化好的 twork 结构添加到 task->task_works 链表上。task 可以是任意task。若参数 notify 为真,还会判断 若 task 是其它CPU上正在运行的任务,则触发其resched。
当任务正处于退出状态时,是不允许向其添加 work 的。
2. task_work_cancel
将之前添加到 task->task_works 链表上的 work 给移除掉。
3. task_work_run
在返回用户空间时,遍历 task->task_works 链表上的所有 work, 调用其回调函数。同时将 current->task_works = NULL, 保证每次queue work只会被执行一次。
调用路径:
ret_to_user //entry.S 返回用户空间路径 work_pending //entry.S do_notify_resume //signal.c 如果 if (thread_flags & _TIF_NOTIFY_RESUME) 成立则调用 tracehook_notify_resume //tracehook.h 判断 current->task_works 不为NULL则调用 task_work_run //task_work.c
实际上,主要是在get_signal()中被调用,因为它先执行:
ret_to_user //entry.S 等效于 if (current->thread_info.flags & _TIF_WORK_MASK != 0) 则调用 work_pending //entry.S arg1=regs arg2=current->thread_info.flags do_notify_resume //signal.c 若flags中包含 _TIF_SIGPENDING 则执行 uprobe_notify_resume do_signal get_signal task_work_run //先执行 tracehook_notify_resume task_work_run //后执行 fpsimd_restore_current_state
4. exit_task_work
在任务退出时也会调用一遍 current->task_works 链表上的所有回调。
调用路径:
do_exit //exit.c exit_task_work(currrent) //task_work.c task_work_run //task_work.c
四、task_work内核使用案例
1. binder中使用
用来延迟关闭fd.
binder_transaction //binder.c 失败路径中调用 binder_free_buf //binder.c 释放buffer binder_transaction_buffer_release binder_thread_read //binder.c binder_apply_fd_fixups binder_deferred_fd_close(fd) //binder.c struct binder_task_work_cb *twcb = kzalloc(sizeof(*twcb), GFP_KERNEL); init_task_work(&twcb->twork, binder_do_fd_close); //回调中执行 fput(twcb->file) if (twcb->file) task_work_add(current, &twcb->twork, true); else kfree(twcb);
2. signal.c中有调用
ptrace_notify //signal.c task_work_run get_signal //signal.c task_work_run
五、task_work实验
1. 实验代码
#include <linux/task_work.h> struct task_work_struct_t { struct callback_head tw; int data; }; struct task_work_struct_t tws = { .data = 12345, }; static void task_work_cb_func(struct callback_head *tw) { struct task_work_struct_t *t = container_of(tw, struct task_work_struct_t, tw); pr_info("%s: t->data=%d\n", __func__, t->data); dump_stack(); }
2. log打印
[ 125.675609] (6)[7259:cat]HAM: task_work_cb_func: t->data=12345 [ 125.700732] (6)[7259:cat]Call trace: [ 125.704504] (6)[7259:cat] dump_backtrace+0x0/0x1f4 [ 125.709515] (6)[7259:cat] show_stack+0x20/0x2c [ 125.714176] (6)[7259:cat] dump_stack+0xe4/0x134 [ 125.718920] (6)[7259:cat] task_work_cb_func+0x30/0x3c [ 125.724191] (6)[7259:cat] task_work_run+0x40/0xc4 [ 125.729116] (6)[7259:cat] do_notify_resume+0x618/0x10fc [ 125.734574] (6)[7259:cat] work_pending+0x8/0x14
可以看到 task_work 的回调被执行了,每次cat文件节点,对应的read系统调用会被调用两次, 有两次返回用户空间,此栈回溯打印两次。
posted on 2025-05-19 16:15 Hello-World3 阅读(76) 评论(0) 收藏 举报