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_ysptr
和 p_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 结尾
关于 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_ysptr
和 p_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
.