探索Linux 内核创建一个新进程的过程-树莓派实现

描述进程的数据结构

在Linux内核中使用task_struct这个结构体来描述进程。完整代码可以见详细代码,task_struct结构非常复杂,里面有进程的状态,进程双向链表管理等等。
以下为Linux内核管理进程状态转换图
在这里插入图片描述
进程有五个状态,进程的创建-进程的就绪状态-进程的阻塞状态-进程的运行状态-进程的终止状态。
首先使用fork方法来创建一个子进程,注意这里有两个TASK_RUNNING但不同的是一个是就绪,一个是运行,请注意那段running on CPU也就是说在CPU中执行的就是运行态,被内核调度出去,在等待队列中的就是就绪态。如果等待某些特定资源就会进入阻塞态,在TASK_INTERRUPTIBLE时可以被信号和wake_up()唤醒,进程可变为TASK_RUNNING,如果是TASK_UNINTERRUPTIBLE,只能被wake_up()唤醒。
在task_struct中state,stack和双向链表(把所有进程用双向链表连接起来)都很重要。

分析fork

对于fork的完整代码可见代码,fork是一个系统调用,实际就是把当前进程又复制一个子进程,一变二,所有使用n次fork就会有2的n次方个进程。注意父进程和子进程的返回值是不同的。

/*
 * Create a kernel thread.
 */
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
	return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,
		(unsigned long)arg, NULL, NULL);
}

#ifdef __ARCH_WANT_SYS_FORK
SYSCALL_DEFINE0(fork)
{
#ifdef CONFIG_MMU
	return do_fork(SIGCHLD, 0, 0, NULL, NULL);
#else
	/* can not support in nommu mode */
	return -EINVAL;
#endif
}
#endif

#ifdef __ARCH_WANT_SYS_VFORK
SYSCALL_DEFINE0(vfork)
{
	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0,
			0, NULL, NULL);
}
#endif

#ifdef __ARCH_WANT_SYS_CLONE
#ifdef CONFIG_CLONE_BACKWARDS
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
		 int __user *, parent_tidptr,
		 int, tls_val,
		 int __user *, child_tidptr)
#elif defined(CONFIG_CLONE_BACKWARDS2)
SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags,
		 int __user *, parent_tidptr,
		 int __user *, child_tidptr,
		 int, tls_val)
#elif defined(CONFIG_CLONE_BACKWARDS3)
SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp,
		int, stack_size,
		int __user *, parent_tidptr,
		int __user *, child_tidptr,
		int, tls_val)
#else
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
		 int __user *, parent_tidptr,
		 int __user *, child_tidptr,
		 int, tls_val)
#endif
{
	return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
}
#endif

可以从以上代码中看出fork,vfork和clone三个系统调用(System)以及内核函数kernel_thread都有一个return do_fork即都和do_fork有关。对于do_fork可以见
在这里插入图片描述
在do_fork参数中有一个clone_flags是子进程创建相关标志,通过这个标志对父进程资源进行有选择复制(比如子程序用户态堆栈地址,指向pt_regs结构体的指针,用户态栈大小以及父子进程用户态下pid地址等)。
do_fork主要是为了完成调用copy_process()来复制父进程信息,获得pid,调用wake_up_new_task将子进程进入等待队列,并获得CPU分配的资源,通过clone_flags标志做一些辅助工作。
在这里插入图片描述
copy_process函数为了完成调用dup_task_struct,来复制父进程描述符task_struct,信息检查,初始化,把进程状态复制为TASK_RUNNING(子进程置为就绪态),采用写时复制逐一复制所有其他进程资源,调用copy_thread初始化子进程内核栈,设置子进程pid等。总的来说dup_task_struct就是复制父进程描述符task_struct,pid等,copy_thread初始化子进程内核栈。
在这里插入图片描述
对于copy_thread代码详见代码
也就是说进程的创建过程就是

1.复制进程描述符(task_struct)
2.通过写时复制一一复制其他进程资源
3.分配子进程内核堆栈
4.对内核堆栈关键信息进行初始化

在这里插入图片描述
fork()实际为sys_clone()

使用gdb调试

首先git clone一个内核

git clone https://github.com/mengning/menu.git

然后进入menu文件夹,将test_fork.c替代test.c,可以使用命令mv,我们可以进入test_fork.c可以看到一个Fork函数,里面有wait和fork,逻辑应该很简单
在这里插入图片描述
以防报错,最好把TimeAsm函数以及main函数里MenuConfig关于TimeAsm的一行注释掉。
在这里插入图片描述
在这里插入图片描述
然后输入make rootfs指令。
在这里插入图片描述
再开一个端口

cd ~/linux-5.0.1
gdb
file vmlinux
target remote:1234

设置断点sys_clone,do_fork,copy_process,dup_task_struct,copy_thread和ret_from_fork。
在这里插入图片描述
这里无法设置sys_clone断点,暂不知道原因。
在这里插入图片描述
可以看到do_fork函数里有copy_process函数。
在这里插入图片描述
在这里插入图片描述

总结

这次学习了Linux中进程的创建过程,Linux进程创建依托于fork函数,fork实际就是调用系统调用函数sys_clone。进程创建大致有四个步骤,第一是复制父进程的进程标志符(task_struct),第二一一复制其他进程的资源,第三创建子进程的内核栈,第四对子进程的内核栈中关键信息进行初始化。和创建进程关联很大的函数有do_fork(),copy_process,dup_task_struct,copy_thread和ret_form_fork。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值