zer0stimulus
zer0stimulus

Reputation: 23626

Kernel: how to iterate the children of the current process?

In Linux Kernel Development, 3rd ed, this code was given for traversing the children of the current process.

list_for_each(list, &current->children) {
    task = list_entry(list, struct task_struct, sibling);
    /* task now points to one of current’s children */
}

The "sibling" in this idiom looks out of place. What is its purpose?

Upvotes: 7

Views: 9615

Answers (5)

effolkronium
effolkronium

Reputation: 553

Do not forget to hold RCU lock while accessing not current task_struct. Processes can exit and list might change during iteration if no lock acquired

struct task_struct *task;
rcu_read_lock();                                                    
for_each_process(task) {                                             
      task_lock(task);                                             

      /* do something with your task :) */

      task_unlock(task);                                           
}                                                                    
rcu_read_unlock(); 

Upvotes: 0

yijiem
yijiem

Reputation: 369

I tested zer0stimulus's code with a parent process and 2 children processes. It shows the following children processes list structure:

       ----------          ---------          ---------
 (1)  |          |  next  |         |  next  |         |  (1)
----> | children | -----> | sibling | -----> | sibling | ---->
<---- |          | <----- |         | <----- |         | <----
 (2)  |          |  prev  |         |  prev  |         |  (2)
       ----------          ---------          ---------
        current         child process 1    child process 2

(1) is the next pointer in the sibling of the second child process.
(2) is the prev pointer in the children of the current process (parent process).

I'm running on CentOS 6.10 kernel version: 2.6.32-754.el6.x86_64. The code sample involves a proc fs entry and a userspace program.

Proc fs entry:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>

int read_proc(char* buf, char** start, off_t offset,
              int count, int* eof, void* data) {
  int len = 0;
  struct task_struct* task;
  struct list_head* list;

  printk(KERN_INFO "head: %p", &current->children);
  list_for_each(list, &current->children) {
    printk(KERN_INFO "list: %p, list->next: %p, list->prev: %p",
           list, list->next, list->prev);
    task = list_entry(list, struct task_struct, sibling);
    printk(KERN_INFO "%s %d", task->comm, task->pid);
    len += sprintf(buf + len, "%s %d\n", task->comm, task->pid);
  }
  return len;
}

int function_init(void) {
  create_proc_read_entry("ps_children_list", 0, NULL, read_proc, NULL);
  return 0;
}

void function_cleanup(void) {
  remove_proc_entry("ps_children_list", NULL);
}

module_init(function_init);
module_exit(function_cleanup);

Userspace program (no proper error handling):

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main() {
  printf("pid: %d\n", getpid());
  int pipefd[2];
  pipe(pipefd);
  pid_t pid = fork();
  if (pid == 0) {
    // first child
    sleep(5);
    return 0;
  }
  // parent
  printf("first child pid: %d\n", pid);
  pid = fork();
  if (pid == 0) {
    // second child
    sleep(5);
    return 0;
  }
  printf("second child pid: %d\n", pid);
  char buf[1024];
  int fd = open("/proc/ps_children_list", O_RDONLY);
  ssize_t sz = read(fd, buf, sizeof(buf));
  buf[sz] = '\0';
  printf("buf: %s\n", buf);

  int status = 0;
  wait(&status);
  wait(&status);
  return 0;
}

And the result from dmesg shows:

head: ffff8801981239e8
list: ffff88019802c518, list->next: ffff88021a5639f8, list->prev: ffff8801981239e8
test 5568
list: ffff88021a5639f8, list->next: ffff8801981239e8, list->prev: ffff88019802c518
test 5569

c518 is the address of the first sibling, whose prev pointer points to children (39e8) and whose next pointer points to the second sibling (39f8). The second sibling's next pointer points back to the children (39e8).

Upvotes: 2

Hemant P
Hemant P

Reputation: 1

Here is how I understood this:

The lists always point to sibling member,

1) When iterating through children list, we traverse sibling members of current task's children

2) Iterating sibling list will make traversing sibling members of parent's children (or current task's siblings)

Using sibling member to add to parent's list ensures that 2 lists - parent's children and current task's sibling list - are updated with same member.

Upvotes: 0

steve
steve

Reputation: 6020

sibling is a field in the task_struct.

Upvotes: 1

caf
caf

Reputation: 239111

sibling is the name of the list_head structure in struct task_struct that corresponds to the parent's children list.

That is, in this loop list always points to a sibling member of a struct task_struct, or the children member of the parent.

Upvotes: 10

Related Questions