Adam
Adam

Reputation: 10036

looping over a process's parents, Linux kernel

The following code loops over the ancestors of curr_task, all the way to the swapper process (i.e the "most distant" ancestor of any Linux process), where it stops because swapper == swapper->parent.

while (curr_task != curr_task->parent)
       ...get curr_task info
       curr_task = curr_task->parent

The problem is I also want to get the swapper process's info. There are a couple obvious ways to do this (eg goto statement after the while loop, or a single if statement after the loop that gets the swapper info). However, these obvious solutions seems fairly inelegant, and so I'm looking for a more concise solution that doesn't involve the much-maligned goto or copy/pasting code.

Upvotes: 1

Views: 1877

Answers (3)

Harry
Harry

Reputation: 11648

Use some functions to do it as follows.

walk_tasks_backwards_from(current);

Functions follow...

static void walk_tasks_backwards_from(struct task_struct *pos);
static void process_tasks(struct task_struct *parent, struct task_struct *child);

static void process_tasks(struct task_struct *parent, struct task_struct *child) {
  pr_alert("parent process: %s, PID: %d\n", parent->comm, parent->pid);
  pr_alert("child  process: %s, PID: %d\n", child->comm , child->pid);
}
 
static void walk_tasks_backwards_from(struct task_struct *pos) {
  struct task_struct *parent; ; 
  do {
    parent = pos->parent;
    process_tasks(parent, pos);
    pos = parent;
  } while(parent->pid != 0); 
}

You call the function using current or any task you want to walk from ie

  walk_tasks_backwards_from(current);

This produces the following output on my machine where I have a toy kernel module that I can trigger using cat /dev/toy

[161769.300609] parent process: bash, PID: 918
[161769.301299] child  process: cat, PID: 2803 
[161769.301973] parent process: login, PID: 626
[161769.302632] child  process: bash, PID: 918
[161769.303216] parent process: systemd, PID: 1
[161769.303797] child  process: login, PID: 626
[161769.304355] parent process: swapper/0, PID: 0
[161769.304978] child  process: systemd, PID: 1

Alternatively you can use any of the following...

Use a do {} while

task = current;// Get current process
printk(KERN_INFO "process: %s, PID: %d", task->comm, task->pid);
do {

  task = task->parent;
  printk(KERN_INFO "process: %s, PID: %d", task->comm, task->pid);

} while (task->pid != 0);
//task here has pid == 0;

Or use a while loop.

task = current;// Get current process
while (task->pid != 0) {

  printk(KERN_INFO "process: %s, PID: %d", task->comm, task->pid);
  task = task->parent;
  
}
//task here has pid == 0;
printk(KERN_INFO "process: %s, PID: %d", task->comm, task->pid);

Loop over everything

struct task_struct task;
for_each_process(task) {
  printk(KERN_INFO "process: %s, PID: %d", task->comm, task->pid);
}

Upvotes: 2

firo
firo

Reputation: 1062

In fact, we need RCU lock to protect us from crash:

void dump_current_ancestors(void)
{
    struct task_struct *tmp;

    printk("Comm    PID     TID\n");
    rcu_read_lock();
    tmp  = current;
    do {
            printk("%s      %d      %d\n", tmp->comm, tmp->tgid, tmp->pid);
    } while ((tmp != tmp->real_parent) && (tmp = tmp->real_parent));
    rcu_read_unlock();
    printk("-------end---------\n");

}`

Upvotes: 1

Nate Eldredge
Nate Eldredge

Reputation: 58172

Here's one way to do it: use another variable.

struct task_struct *t, *prev;
t = current;
do {
    // do something with t
    prev = t;
    t = t->parent;
} while (prev->pid != 0);

Or move that into a function:

int move_up(struct task **tp) {
    int was_swapper = ((*tp)->pid == 0);
    *tp = (*tp)->parent;
    return was_swapper;
}

// ...

struct task_struct *t = current;
do {
     // do something with t
} while (move_up(&t));

Upvotes: 2

Related Questions