Linux源码剖析--fork()

本文详细剖析了Linux系统调用fork()的工作原理,包括创建进程控制块(PCB),分配PID,复制进程描述符,文件描述符,内存空间,线性区和页表,以及内核栈的过程。通过do_fork()和copy_process()等关键函数,阐述了如何实现进程的复制,并特别强调了CLONE_VM标志对共享地址空间的影响。
摘要由CSDN通过智能技术生成

    fork()的功能是复制进程,首先我们要清楚进程是什么。进程是一个正在运行的进程,是资源分配的最小单位。系统对进程的管理是通过对进程控制块(PCB)的管理实现的。每个进程的产生分为两步:1.分配PCB;2.准备进程实体,如分配内存空间等。

    fork()、pthread_create()、vfork()对应的系统调用分别是sys_fork(),sys_clone(),sys_vfork(),它们的底层都是通过do_fork()实现的。

    do_fork()的工作步骤:1.定义PCB指针struct task_struct *p;2.分配PID;3.调用copy_process()方法,创建子进程的进程描述符,即task_struct(重点)。

/**
 * 负责处理clone,fork,vfork系统调用。
 * clone_flags-与clone的flag参数相同
 * stack_start-与clone的child_stack相同
 * regs-指向通用寄存器的值。是在从用户态切换到内核态时被保存到内核态堆栈中的。
 * stack_size-未使用,总是为0
 * parent_tidptr,child_tidptr-clone中对应参数ptid,ctid相同
 */
long do_fork(unsigned long clone_flags,
	      unsigned long stack_start,
	      struct pt_regs *regs,
	      unsigned long stack_size,
	      int __user *parent_tidptr,
	      int __user *child_tidptr)
{
	struct task_struct *p;
	int trace = 0;
	/**
	 * 通过查找pidmap_array位图,为子进程分配新的pid参数.
	 */
	long pid = alloc_pidmap();

	if (pid < 0)
		return -EAGAIN;
	/**
	 * 如果父进程正在被跟踪,就检查debugger程序是否想跟踪子进程.并且子进程不是内核进程(CLONE_UNTRACED未设置)
	 * 那么就设置CLONE_PTRACE标志.
	 */
	if (unlikely(current->ptrace)) {
		trace = fork_traceflag (clone_flags);
		if (trace)
			clone_flags |= CLONE_PTRACE;
	}

	/**
	 * copy_process复制进程描述符.如果所有必须的资源都是可用的,该函数返回刚创建的task_struct描述符的地址.
	 * 这是创建进程的关键步骤.
	 */
	p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);

    do_fork()第三部工作,即调用copy_process()。该函数的功能:创建进程描述符以及子进程执行所需要的所有其他数据结构。

    1.分配PCB,继承父进程中PCB的值,只是将特有的信息改过来。通过dup_task_struct(current)来获取进程描述符(PCB)。在dup_task_struct(current)之前都是对进程的一些判断(检查标志位合法性)和安全性检查。

/**
 * 创建进程描述符以及子进程执行所需要的所有其他数据结构
 * 它的参数与do_fork相同。外加子进程的PID。
 */
static task_t *copy_process(unsigned long clone_flags,
				 unsigned long stack_start,
				 struct pt_regs *regs,
				 unsigned long stack_size,
				 int __user *parent_tidptr,
				 int __user *child_tidptr,
				 int pid)
{
	int retval;
	struct task_struct *p = NULL;

	/**
	 * 检查clone_flags所传标志的一致性。
	 */

	/**
	 * 如果CLONE_NEWNS和CLONE_FS标志都被设置,返回错误
	 */
	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
		return ERR_PTR(-EINVAL);

	/*
	 * Thread groups must share signals as well, and detached threads
	 * can only be started up within the thread group.
	 */
	/**
	 * CLONE_THREAD标志被设置,并且CLONE_SIGHAND没有设置。
	 * (同一线程组中的轻量级进程必须共享信号)
	 */
	if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
		return ERR_PTR(-EINVAL);

	/*
	 * Shared signal handlers imply shared VM. By way of the above,
	 * thread groups also imply shared VM. Blocking this case allows
	 * for various simplifications in other code.
	 */
	/**
	 * CLONE_SIGHAND被设置,但是CLONE_VM没有设置。
	 * (共享信号处理程序的轻量级进程也必须共享内存描述符)
	 */
	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
		return ERR_PTR(-EINVAL);

	/**
	 * 通过调用security_task_create以及稍后调用security_task_alloc执行所有附加的安全检查。
	 * LINUX2.6提供扩展安全性的钩子函数,与传统unix相比,它具有更加强壮的安全模型。
	 */
	retval = security_task_create(clone_flags);
	if (retval)
		goto fork_out;

	retval = -ENOMEM;
	/**
	 * 调用dup_task_struct为子进程获取进程描述符。
	 */
	p = dup_task_struct(current);

    每个进程都有thread_info指针,thread_info结构体保存的是进程上下文的信息。要修改thread_info *info,子进程的task_struct的成员struct thread_info *info指向自己的struct thread_info,而且struct thread_info结构体的成员struct task_struct *p指向子进程自己的struct task_struct。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值