Siblings of `struct task_struct current` always include a process with `pid = 0`

Viewed 238

I'm hacking the linux kernel and playing with siblings and children of the struct task_struct current.

When outputting the pid and command name of siblings, there appears to be a malformed process with pid = 0 and the command name is gibberish.

The same thing occurs with the process' parents.

Why is there a process with pid=0 showing up among the siblings? Isn't that process reserved for swapper?

Code

// Loop over process and parents using something like:

/*
printk("--syscall ## Begin process results ##"); 

printk("--syscall // View children //");
my_list_head = &(current->children);

printk("--syscall // View siblings //");
my_list_head = &(current->sibling)

printk("--syscall results:  ...");
*/


if (my_list_head == NULL) {
  return 0;
}

list_for_each(tempNode, my_list_head) {
  tempTask = list_entry(tempNode, struct task_struct,
          sibling);
  printk("--syscall The %ld-th process's pid is %d and command %s",
         count, tempTask->pid, tempTask->comm);
 }

Output

Formatted w/ whitespace

[ 2938.994084] --syscall ## Begin process results ##
[ 2938.994089] --syscall // View children //
[ 2938.994105] --syscall // View siblings //
[ 2938.994116] --syscall The 1-th process's pid is 0 and command \x80ݶE\x96\xff\xff
[ 2938.994133] --syscall results: pid=1400 name=process_ancesto state=0 uid=1000 nvcsw=1 nivcsw=0 num_children=0 num_siblings=1

[ 2938.994139] --syscall ## Begin process results ##
[ 2938.994144] --syscall // View children //
[ 2938.994149] --syscall The 1-th process's pid is 1400 and command process_ancesto
[ 2938.994158] --syscall // View siblings //
[ 2938.994163] --syscall The 1-th process's pid is 0 and command
[ 2938.994176] --syscall results: pid=1282 name=bash state=1 uid=1000 nvcsw=88 nivcsw=18 num_children=1 num_siblings=1

[ 2938.994180] --syscall ## Begin process results ##
[ 2938.994185] --syscall // View children //
[ 2938.994190] --syscall The 1-th process's pid is 1282 and command bash
[ 2938.994198] --syscall // View siblings //
[ 2938.994203] --syscall The 1-th process's pid is 1275 and command systemd
[ 2938.994210] --syscall The 2-th process's pid is 0 and command
[ 2938.994216] --syscall The 3-th process's pid is 117 and command systemd-journal
[ 2938.994222] --syscall The 4-th process's pid is 145 and command systemd-udevd
[ 2938.994227] --syscall The 5-th process's pid is 148 and command systemd-network
[ 2938.994233] --syscall The 6-th process's pid is 369 and command systemd-resolve
[ 2938.994239] --syscall The 7-th process's pid is 370 and command systemd-timesyn
[ 2938.994245] --syscall The 8-th process's pid is 412 and command accounts-daemon
[ 2938.994321] --syscall The 9-th process's pid is 413 and command dbus-daemon
[ 2938.994336] --syscall The 10-th process's pid is 417 and command irqbalance
[ 2938.994346] --syscall The 11-th process's pid is 418 and command rsyslogd
[ 2938.994352] --syscall The 12-th process's pid is 419 and command snapd
[ 2938.994359] --syscall The 13-th process's pid is 420 and command systemd-logind
[ 2938.994365] --syscall The 14-th process's pid is 439 and command cron
[ 2938.994372] --syscall The 15-th process's pid is 451 and command atd
[ 2938.994378] --syscall The 16-th process's pid is 456 and command agetty
[ 2938.994385] --syscall The 17-th process's pid is 461 and command sshd
[ 2938.994390] --syscall The 18-th process's pid is 491 and command unattended-upgr
[ 2938.994397] --syscall The 19-th process's pid is 501 and command polkitd
[ 2938.994413] --syscall results: pid=1200 name=login state=1 uid=0 nvcsw=31 nivcsw=33 num_children=1 num_siblings=19
1 Answers

Here is an illustration of how two sibling child processes are linked into their parent process's list of children:

     PARENT              CHILD 1             CHILD 2
     ======              =======             =======

                         task_struct         task_struct
                        +-------------+     +-------------+
                        |             |     |             |
     task_struct        ~             ~     ~             ~
    +-------------+     |             |     |             |
    |             |     |-------------|     |-------------|
    ~             ~     | children    |     | children    |
    |             |     |             |     |             |
. . |-------------| . . |-------------| . . |-------------| . .
    | children    |     | sibling     |     | sibling     |
X==>| prev | next |<===>| prev | next |<===>| prev | next |<==X
. . |-------------| . . |-------------| . . |-------------| . .
    | sibling     |     |             |     |             |
    |             |     ~             ~     ~             ~
    |-------------|     |             |     |             |
    |             |     +-------------+     +-------------+
    ~             ~
    |             |     'X's are joined together, making
    +-------------+     a doubly linked, circular list.

Although children and sibling are both of type struct list_head, children is being used as an actual list head (linking to its list of child processes), whereas sibling is being used as a list entry.

The parent's children.next link points to child 1's sibling member, child 1's sibling.next link points to child 2's sibling member, and child 2's sibling.next link points back to the parent's children member (the list head). Similarly, the parent's children.prev link points to child 2's sibling member, child 2's sibling.prev link points to child 1's sibling member, and child 1's sibling.prev link points back to the parent's children member.

The list_for_each(pos, head) macro visits each node pos in the list, starting at head->next, while pos != head.

Normally, the head parameter of list_for_each(pos, head) should be an actual list head, but the macro cannot tell the difference between a list head and a list entry. They are both the same type and all the nodes are linked together circularly. (The whole list consists of a list head with zero or more list entries linked in a circle. For an empty list, the list head just links back to itself.) The list_for_each macro will just iterate around the doubly linked list until it get back to where it started.

If list_for_each(pos, head) was called with head pointing to the parent's children member, then pos would point to child 1's sibling member in the first iteration, and would point to child 2's sibling member in the second iteration, and would terminate the loop with pos pointing back to the parent's children member. Inside the loop, list_entry(pos, struct task_struct, sibling) would correctly point to the beginning of the struct task_struct for the child process.

Let us say that child 1 is the current process. OP's code is using list_for_each(pos, head) with head pointing to child 1's sibling member. Therefore, pos would point to child 2's sibling member in the first iteration, and would point to the parent's children member in the second iteration, and would terminate the loop with pos pointing back to child 1's sibling member. Inside the loop, list_entry(pos, struct task_struct, sibling) would correctly point to the beginning of child 2's struct task_struct in the first iteration, but pos would point to somewhere before the beginning of the parent's struct task_struct in the second iteration. That is the where the problem lies in OP's code.

Related