Linux 2.4中,最小的子链表是循环的吗?

In Linux 2.4, is the youngest child list cylic?

关于 task_struct 个列表:

/* 
 * pointers to (original) parent process, youngest child, younger sibling,
 * older sibling, respectively.  (p->father can be replaced with 
 * p->p_pptr->pid)
 */
task_t *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;

我正在通过进程的子进程使用这些指向 运行 的指针。 我不明白(并且很难从内核代码中理解)是最小的子列表是否以 null 结尾还是循环?

我可以 运行 完成所有 p_cptr 直到我到达 NULL,还是我应该期待再次回到头部?

试试下面的代码。我没有在内核 2.4 上测试过它(仅在 4.0 上),但我认为它应该适用于 2.4,只需稍作修改。例如,我使用 kthreadd 进程作为父进程,因为它有很多子进程。

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/list.h>

#define KTHREADD_PID 2

static int __init ktdf_init(void)
{
    struct task_struct *kthreadd_task;
    struct list_head *list;

    /* Find kthreadd task by PID */
    kthreadd_task = pid_task(find_vpid(KTHREADD_PID), PIDTYPE_PID);
    pr_debug("Process name: %s\n", kthreadd_task->comm);

    /* Iterate over all children of kthreadd_task */
    list_for_each(list, &kthreadd_task->children) {
        struct task_struct *task;

        /* Get next child */
        task = list_entry(list, struct task_struct, sibling);
        pr_debug("  Child name: %s\n", task->comm);
    }

    return 0;
}

static void __exit ktdf_exit(void)
{
}

module_init(ktdf_init);
module_exit(ktdf_exit);

MODULE_AUTHOR("Sam Protsenko");
MODULE_DESCRIPTION("kthreadd children finder module");
MODULE_LICENSE("GPL");

所以如您所见,.sibling 列表是 circular doubly-linked list, which means the last element of this list (tail) points to the first element (head). You can see it from list_for_each() 宏实现。

dmesg中的输出(模块被insmod加载后):

Process name: kthreadd
  Child name: ksoftirqd/0
  Child name: ksoftirqd/1
  Child name: ksoftirqd/2
  Child name: ksoftirqd/3
  Child name: kworker/0:0
  Child name: kworker/0:0H
  Child name: kworker/0:1
  Child name: kworker/0:1H
  Child name: kworker/0:2
  ...

可以通过ps命令查看:

$ ps auxf

哪个给了我相同的进程树:

[kthreadd]
\_ [ksoftirqd/0]
\_ [ksoftirqd/1]
\_ [ksoftirqd/2]
\_ [ksoftirqd/3]
\_ [kworker/0:0]
\_ [kworker/0:0H]
\_ [kworker/0:1]
\_ [kworker/0:1H]
\_ [kworker/0:2]
...

至少在 2.4.18 中,您感兴趣的两个列表都不是循环的(即由 p_cptr 字段组成的列表,以及由 p_ysptrp_osptr 个字段)。


我从 do_fork 中的 kernel/fork.c 中找到的一些代码推断出这一点(主分支程序):

p = alloc_task_struct();
...
*p = *current;
...
p->p_cptr = NULL;
...
SET_LINKS(p);

在新生进程中,我们为其分配一个task_struct,将parent进程的task_struct复制到其中。
第三行以 NULL.

终止 p_cptr 列表


SET_LINKS 定义在 include/linux/sched.h:

#define SET_LINKS(p) do { \
    ...
    (p)->p_ysptr = NULL; \
    if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \
        (p)->p_osptr->p_ysptr = p; \
    (p)->p_pptr->p_cptr = p; \
    ...
} while (0)

新生儿总是最年轻的child,所以当SET_LINKS将其p_ysptr设置为NULL时,它终止了最年轻的child端(的兄弟姐妹名单) NULL.

如果新生儿不是第一个(即最老的)child,那么 SET_LINKS 不会改变最老 child 的 p_osptr
但是如果新生儿是第一个 child,(p)->p_pptr->p_cptr 仍然是 NULL
(p)->p_osptr = (p)->p_pptr->p_cptr 被执行,因此第一个 child 的 p_osptr (最旧的 child)被设置为 NULL.
因此,SET_LINKS 也以 NULL.

终止列表中最早的 child 结尾