解析 linux 进程 pid 0, pid 1, pid 2 关系及启动过程


本文以 Linux v2.6.39 为例,说明 Linux中这三个进程的关系及启动过程。

三个进程的关系

Linux 中有pid 0, pid 1 和 pid 2 三个特殊的进程。

  • pid 0,即 “swapper” 进程,是 pid 1 和 pid 2 的父进程。
  • pid 1,即 “init” 进程,所有用户空间的进程均派生自该进程。
  • pid 2,即 “kthreadd” 进程,是内核空间所有进程的父进程。

除了pid 0为静态生成外,其他进程实际都是调用 do_fork 生成。

$ ps -ef | head -n 3
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 09:20 ?        00:00:13 /sbin/init auto noprompt
root           2       0  0 09:20 ?        00:00:00 [kthreadd]

要注意,编号1,2 不是特地为这两个进程保留,而是按照生成进程的顺序分配得来。

调用链

INIT_TASK		/* pid 0 */

start_kernel
	rest_init
		kernel_thread		
			kernel_init			/* pid 1 */
				init_post
					run_init_process
		kernel_thread	
			kthreadd			/* pid 2 */

具体启动过程

进程 pid 0 通过代码静态生成

/arch/x86/kernel/init_task.c

struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);

INIT_TASK 宏定义如下:

/include/linux/init_task.h

#define INIT_TASK(tsk)	\
{									\
	.state		= 0,						\
	.stack		= &init_thread_info,				\
	/* 设置默认优先级 */
	.prio		= MAX_PRIO-20,					\
	.static_prio	= MAX_PRIO-20,					\
	.normal_prio	= MAX_PRIO-20,					\
	.policy		= SCHED_NORMAL,					\
	.cpus_allowed	= CPU_MASK_ALL,					\
	.mm		= NULL,						\
	.active_mm	= &init_mm,					\
	...
	.tasks		= LIST_HEAD_INIT(tsk.tasks),			\
	
	/* 设置进程间的关系,tsk为当前进程,也就是这里父进程为自身 */
	.real_parent	= &tsk,						\
	.parent		= &tsk,						\
	.children	= LIST_HEAD_INIT(tsk.children),			\
	.sibling	= LIST_HEAD_INIT(tsk.sibling),			\
	.group_leader	= &tsk,						\
	
	.comm		= "swapper",					\
	.thread		= INIT_THREAD,					\
	...
}

进程 pid 1 和 pid 2 由 pid 0 生成

生成 pid 1 和 pid 2

要注意,编号1,2 不是特地为这两个进程保留,而是按照生成进程的顺序分配得来。
/init/main.c

asmlinkage void __init start_kernel(void)
{
	...
	mm_init_owner(&init_mm, &init_task);
	...
	rest_init();
}

static noinline void __init_refok rest_init(void)
{
	 /* 生成 pid 1 */
	kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
	/* 生成 pid 2 */
	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

	/* 进程 pid 0 变成 idle 进程,不做任何实际工作,只在没有进程运行的时候运行 */
	cpu_idle();
}

kernel_thread 函数定义

注意,这里设置段寄存器指向内核。
/arch/x86/kernel/process.c

int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
	struct pt_regs regs;

	memset(&regs, 0, sizeof(regs));

	regs.si = (unsigned long) fn;
	regs.di = (unsigned long) arg;

	/* 设置段描述符寄存器 */
#ifdef CONFIG_X86_32
	regs.ds = __USER_DS;
	regs.es = __USER_DS;
	regs.fs = __KERNEL_PERCPU;
	regs.gs = __KERNEL_STACK_CANARY;
#else
	regs.ss = __KERNEL_DS;
#endif

	regs.orig_ax = -1;
	regs.ip = (unsigned long) kernel_thread_helper;
	regs.cs = __KERNEL_CS | get_kernel_rpl();
	regs.flags = X86_EFLAGS_IF | 0x2;

	return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}

进程 pid 1 的生成函数

static int __init kernel_init(void * unused)
{
	init_post();
	return 0;
}

static noinline int init_post(void)
{
	/* 逐个尝试执行不同的 "init" 指令,
	 * 如果执行成功,则当前进程的text字段就被替换,后续命令也会被覆盖
	 */
	run_init_process("/sbin/init");
	run_init_process("/etc/init");
	run_init_process("/bin/init");
	run_init_process("/bin/sh");

	panic("No init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}

static void run_init_process(const char *init_filename)
{
	argv_init[0] = init_filename;
	kernel_execve(init_filename, argv_init, envp_init);
}

进程 pid 2 的生成函数

pid 2 的 comm 字段为 “kthreadd”.
/kernel/kthread.c

int kthreadd(void *unused)
{
	struct task_struct *tsk = current;

	/* Setup a clean context for our children to inherit. */
	set_task_comm(tsk, "kthreadd");
	ignore_signals(tsk);
	set_cpus_allowed_ptr(tsk, cpu_all_mask);
	set_mems_allowed(node_states[N_HIGH_MEMORY]);

	current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;

	for (;;) {
		set_current_state(TASK_INTERRUPTIBLE);
		if (list_empty(&kthread_create_list))
			schedule();
		__set_current_state(TASK_RUNNING);

		spin_lock(&kthread_create_lock);
		
		/* 只要 kthread_create_list 不为空
		 * 就通过 create_thread 创建新的进程
		 * 因此 通过 kthread_create_list 创建的进程均为其子进程
		 */
		while (!list_empty(&kthread_create_list)) {
			struct kthread_create_info *create;

			create = list_entry(kthread_create_list.next,
					    struct kthread_create_info, list);
			list_del_init(&create->list);
			spin_unlock(&kthread_create_lock);

			create_kthread(create);

			spin_lock(&kthread_create_lock);
		}
		spin_unlock(&kthread_create_lock);
	}

	return 0;
}
  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lylhw13_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值