Efficient way to find task_struct by pid

27,955

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.

Share:
27,955
zer0stimulus
Author by

zer0stimulus

Updated on April 02, 2022

Comments

  • zer0stimulus
    zer0stimulus 9 months

    Is there an efficient way of finding the task_struct for a specified PID, without iterating through the task_struct list?

  • firo
    firo about 8 years
    Could you tell me the meaning of v in vpid?
  • Abdullah over 7 years
    i have test the above function and the kernel panics
  • mdd
    mdd over 7 years
    I 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 years
    nice - that is the function I was looking for! :)
  • red0ct
    red0ct almost 3 years
    @Abdullah Perhaps it was BUG: unable to handle kernel NULL pointer dereference. See my answer.
  • Chan Kim
    Chan Kim over 2 years
    v 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
    Tan Nguyen about 1 year
    How 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
    Tan Nguyen about 1 year
    Instead, 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
    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
    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
    shashank arora 12 months
    Hi @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.