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)    收藏  举报

导航