Efficient way to find task_struct by pid
Solution 1
What's wrong with using one of the following?
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);
Solution 2
If you want to find the task_struct
from a module, find_task_by_vpid(pid_t nr)
etc. are not going to work since these functions are not exported.
In a module, you can use the following function instead:
pid_task(find_vpid(pid), PIDTYPE_PID);
Solution 3
There is a better way to get the instance of task_struct from a module. Always try to use wrapper function/ helper routines because they are designed in such a way if driver programmer missed something, the kernel can take care by own. For eg - error handling, conditions checks etc.
/* Use below API and you will get a pointer of (struct task_struct *) */
taskp = get_pid_task(pid, PIDTYPE_PID);
and to get the PID of type pid_t. you need to use below API -
find_get_pid(pid_no);
You don't need to use "rcu_read_lock()" and "rcu_read_unlock()" while calling these API's because "get_pid_task()" internally calls rcu_read_lock(),rcu_read_unlock() before calling "pid_task()" and handles concurrency properly. That's why I have said above use these kind of wrapper always.
Snippet of get_pid_task() and find_get_pid() function below :-
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);
In a kernel module, you can use wrapper function in the following way as well -
taskp = get_pid_task(find_get_pid(PID),PIDTYPE_PID);
PS: for more information on API's you can look at kernel/pid.c
Solution 4
No one mentioned that the pid_task()
function and the pointer (which you obtain from it) should be used inside RCU critical section (because it uses RCU-protected data structure). Otherwise there can be use-after-free BUG.
There are lots of cases of using pid_task()
in Linux kernel sources (e.g. in posix_timer_event()
).
For example:
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!) */
Find out more about RCU API from Kernel.org
P.S. also you can just use the special API functions like find_task_by_pid_ns()
and find_task_by_vpid()
under the rcu_read_lock()
.
The first one is for searching through the particular namespace:
task = find_task_by_pid_ns(pid_num, &init_pid_ns); /* e.g. init namespace */
The second one is for searching through the namespace of current
task.

zer0stimulus
Updated on April 02, 2022Comments
-
zer0stimulus 9 months
Is there an efficient way of finding the
task_struct
for a specified PID, without iterating through thetask_struct
list? -
firo about 8 yearsCould you tell me the meaning of v in vpid?
-
Abdullah over 7 yearsi have test the above function and the kernel panics
-
mdd over 7 yearsI am using this function successfully on kernel 3.8/x86_64. Could you open a new question and add more details, like the panic message etc.?
-
Chris over 3 yearsnice - that is the function I was looking for! :)
-
red0ct almost 3 years@Abdullah Perhaps it was
BUG: unable to handle kernel NULL pointer dereference
. See my answer. -
Chan Kim over 2 yearsv means virtual. it's explained in include/linux/sched.h. It looks like it was named so because of the 'container' thing. (lwn.net/Articles/168093)
-
Tan Nguyen about 1 yearHow do you know there is a helping function and wrapping multiple helping function together (in your example, find_get_pid)? get_pid_task(find_get_pid(PID),PIDTYPE_PID);. I find no docs on this.
-
Tan Nguyen about 1 yearInstead, I find something like ps -p $PID, ps -p $PID -o pid,vsz=MEMORY -o user,group=GROUP -o comm,args=ARGS. What is this by the way?
-
Tan Nguyen about 1 year@shashank arora (above answer) also using get_pid_task(find_get_pid(PID),PIDTYPE_PID);. Compare to your pid_task(...), what's the difference?
-
red0ct about 1 year@TanNguyen If you will try to read a bit my answer you'll see that the main difference is that one should avoid use-after-free bugs while using RCU.
-
shashank arora 12 monthsHi @red0ct , I have mentioned use get_pid_task() instead of pid_task because with RCU it will help all readers to read the element concurrently even when it is in the process of being updated . I hope tan Nguyen got the answer as well.