【问题标题】:Efficient way to find task_struct by pid通过pid查找task_struct的有效方法
【发布时间】:2012-01-22 18:10:01
【问题描述】:

有没有一种有效的方法可以找到指定 PID 的 task_struct,而无需遍历 task_struct 列表?

【问题讨论】:

    标签: c linux process linux-kernel


    【解决方案1】:

    使用以下其中一种有什么问题?

    extern struct task_struct *find_task_by_vpid(pid_t nr);
    extern struct task_struct *find_task_by_pid_ns(pid_t nr,
                struct pid_namespace *ns);
    

    【讨论】:

    • 你能告诉我vpid中v的含义吗?
    • v 表示虚拟。它在 include/linux/sched.h 中有解释。看起来它之所以如此命名是因为“容器”的东西。 (lwn.net/Articles/168093)
    【解决方案2】:

    如果您想从模块中找到task_structfind_task_by_vpid(pid_t nr) 等将不起作用,因为这些函数没有导出。

    在模块中,您可以使用以下函数:

    pid_task(find_vpid(pid), PIDTYPE_PID);
    

    【讨论】:

    • 我已经测试了上面的函数和内核恐慌
    • 我在内核 3.8/x86_64 上成功使用了这个功能。您能否打开一个新问题并添加更多详细信息,例如紧急消息等?
    • 很好——这就是我正在寻找的功能! :)
    【解决方案3】:

    有一种更好的方法可以从模块中获取 task_struct 的实例。 始终尝试使用包装函数/辅助例程,因为它们的设计方式是如果驱动程序程序员遗漏了什么,内核可以自行处理。例如 - 错误处理、条件检查等。

    /* Use below API and you will get a pointer of (struct task_struct *) */
    
    taskp = get_pid_task(pid, PIDTYPE_PID);
    

    并获取 pid_t 类型的 PID。您需要使用以下 API -

    find_get_pid(pid_no);
    

    在调用这些 API 时不需要使用“rcu_read_lock()”和“rcu_read_unlock()”,因为“get_pid_task()" 在调用 "pid_task()" 之前在内部调用 rcu_read_lock(),rcu_read_unlock() 并正确处理并发。这就是为什么我在上面说过总是使用这种包装器。

    get_pid_task() 和 find_get_pid() 函数片段如下:-

    struct task_struct *get_pid_task(struct pid *pid, enum pid_type type)
    {
        struct task_struct *result;
        rcu_read_lock();
        result = pid_task(pid, type);
        if (result)
            get_task_struct(result);
        rcu_read_unlock();
        return result;
    }
    EXPORT_SYMBOL_GPL(get_pid_task);
    
    struct pid *find_get_pid(pid_t nr)
    {
        struct pid *pid;
    
        rcu_read_lock();
        pid = get_pid(find_vpid(nr));
        rcu_read_unlock();
    
        return pid;
    }
    EXPORT_SYMBOL_GPL(find_get_pid);
    

    在内核模块中,您也可以通过以下方式使用包装函数 -

    taskp = get_pid_task(find_get_pid(PID),PIDTYPE_PID);
    

    PS:有关 API 的更多信息,您可以查看 kernel/pid.c

    【讨论】:

    • 你怎么知道有一个帮助函数并将多个帮助函数包装在一起(在你的例子中,find_get_pid)? get_pid_task(find_get_pid(PID),PIDTYPE_PID);。我找不到这方面的文档。
    • 相反,我发现类似 ps -p $PID, ps -p $PID -o pid,vsz=MEMORY -o user,group=GROUP -o comm,args=ARGS。顺便问一下,这是什么?
    【解决方案4】:

    没有人提到 pid_task() 函数和 指针(您可以从中获得)应该在 RCU 临界区中使用(因为它使用 RCU 保护数据结构)。 否则会出现use-after-free BUG
    在 Linux 内核源代码中使用 pid_task() 的案例很多(例如在 posix_timer_event() 中)。
    例如:

    rcu_read_lock();
    /* search through the global namespace */
    task = pid_task(find_pid_ns(pid_num, &init_pid_ns), PIDTYPE_PID);
    if (task)
        printk(KERN_INFO "1. pid: %d, state: %#lx\n",
               pid_num, task->state); /* valid task dereference */
    rcu_read_unlock(); /* after it returns - task pointer becomes invalid! */
    
    if (task)
        printk(KERN_INFO "2. pid: %d, state: %#lx\n",
               pid_num, task->state); /* may be successful,
                                       * but is buggy (task dereference is INVALID!) */
    

    Kernel.org了解更多关于 RCU API 的信息


    附:您也可以只使用rcu_read_lock() 下的find_task_by_pid_ns()find_task_by_vpid() 等特殊API 函数。

    第一个用于搜索特定的命名空间:

    task = find_task_by_pid_ns(pid_num, &init_pid_ns); /* e.g. init namespace */
    

    第二个是搜索current任务的命名空间。

    【讨论】:

    • @Abdullah 也许是BUG: unable to handle kernel NULL pointer dereference。看我的回答。
    • @shashank arora(以上答案)也使用 get_pid_task(find_get_pid(PID),PIDTYPE_PID);。和你的 pid_task(...) 比较,有什么区别?
    • @TanNguyen 如果您尝试阅读一下我的回答,您会发现主要区别在于在使用 RCU 时应该避免使用后释放错误。
    猜你喜欢
    • 2012-08-08
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 1970-01-01
    • 1970-01-01
    • 2012-04-18
    • 2017-05-26
    • 1970-01-01
    相关资源
    最近更新 更多