【发布时间】:2012-01-22 18:10:01
【问题描述】:
有没有一种有效的方法可以找到指定 PID 的 task_struct,而无需遍历 task_struct 列表?
【问题讨论】:
标签: c linux process linux-kernel
有没有一种有效的方法可以找到指定 PID 的 task_struct,而无需遍历 task_struct 列表?
【问题讨论】:
标签: c linux process linux-kernel
使用以下其中一种有什么问题?
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);
【讨论】:
如果您想从模块中找到task_struct,find_task_by_vpid(pid_t nr) 等将不起作用,因为这些函数没有导出。
在模块中,您可以使用以下函数:
pid_task(find_vpid(pid), PIDTYPE_PID);
【讨论】:
有一种更好的方法可以从模块中获取 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
【讨论】:
没有人提到 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任务的命名空间。
【讨论】:
BUG: unable to handle kernel NULL pointer dereference。看我的回答。