linux源码解析-fork

1.调用fork的过程

在调用fork时,系统将调用宏指令_syscall0,进而,调用0x80号中断,寄存器eax中的值为__NR_fork,这是fork传给int $0x80的唯一的参数。

调用中断“int $0x80”以后,在汇编过程“system_call”中,将通过eax中的值__NR_fork(即2)与4的乘积(计算出相对系统调用表的偏移值)作为相对于系统调用表(sys_call_table)偏移,找到入口:

.long SYMBOL_NAME(sys_fork)

于是,系统流程转向函数sys_fork()。(“arch/i386/kernel/process.c”):

asmlinkage int sys_fork(struct pt_regs regs)

{
    return do_fork(SIGCHLD, regs.esp, &regs);

}

所以调用过程fork->sys_fork->do_fork

最终其实我们要分析的就是do_fork函数


2.do_fork解析

do_fork主要流程:

(1)我们要为子进程分配新的pid参数。 在一开始,该函数定义了一个task_struct类型的指针p,用来接收即将为新进程(子进程)所分配的进程描述符。紧接着使用alloc_pidmap函数为这个新进程分配一个pid。由于系统内的pid是循环使用的,所以采用位图方式来管理。简单的说,就是用每一位(bit)来标示该位所对应的pid是否被使用。分配完毕后,判断pid是否分配成功,其中,EAGAIN表示当前的进程数已经达到了系统规定的上限。

(2)接下来检查当前进程(父进程)的ptrace字段。ptrace是用来标示一个进程是否被另外一个进程所跟踪。所谓跟踪,最常见的例子就是处于调试状态下的进程被debugger进程所跟踪。父进程的ptrace字段非0时说明debugger程序正在跟踪父进程,那么接下来通过fork_traceflag函数来检测子进程是否也要被跟踪。如果trace为1,那么就将跟踪标志CLONE_PTRACE加入标志变量clone_flags中。 通常上述的跟踪情况是很少发生的,因此在判断父进程的ptrace字段时使用了unlikely修饰符。使用该修饰符的判断语句执行结果与普通判断语句相同,只不过在执行效率上有所不同。正如该单词的含义所表示的那样,current->ptrace很少为非0。因此,编译器尽量不会把if内的语句与当前语句之前的代码编译在一起,以增加cache的命中率。与此相反,likely修饰符则表示所修饰的代码很可能发生。

(3)调用copy_process()函数复制进程描述符。如果所有必须的资源都是可用的,则该函数返回刚创建的task_struct描述符的地址。

p = copy_process(clone_flags, stack_start, regs, stack_size,
             child_tidptr, NULL, trace);

(4)如果子进程被跟踪或者设置了CLONE_STOPPED标志,那么通过sigaddset函数为子进程设置挂起信号。其中,signal对应一个unsigned long类型的变量,该变量的每个位分别对应一种信号。具体的操作是,将SIGSTOP信号所对应的那一位置1。并将p所代表的子线程的线程标志置为有挂起信号。在另一个进程把子进程状态恢复成TASK_RUNNING之前,一直保持该状态。

(5)如果没有设置CLONE_STOPPED,就调用 wake_up_new_task 将子进程加入调度器,为之分配 CPU;否则,将子进程的状态设置为TASK_STOPPED。

(6)如果父进程被跟踪,则把子进程的PID存入current的ptrace_message字段并调用ptrace_notify函数使当前进程停止运行,并向当前进程的父进程发送SIGCHLD信号。子进程的祖父进程是跟踪父进程的debugger进程。SIGCHLD信号通知debugger进程:当前进程current已经创建了一个子进程,可以通过current->ptrace_message字段获得该子进程的PID。

(7)如果CLONE_VFORK标志被设置,则通过wait操作将父进程阻塞,把父进程插入等待队列直至子进程调用exec函数或者退出.如果copy_process()在执行的时候发生错误,则先释放已分配的pid;再根据PTR_ERR()的返回值得到错误代码,保存于pid中。



/*
 *  Ok, this is the main fork-routine.
 *
 * It copies the process, and if successful kick-starts
 * it and waits for it to finish using the VM if required.
 */
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;
	long nr;

	/*
	 * Do some preliminary argument and permissions checking before we
	 * actually start allocating stuff
	 */
	if (clone_flags & CLONE_NEWUSER) {
		if (clone_flags & CLONE_THREAD)
			return -EINVAL;
		/* hopefully this check will go away when userns support is
		 * complete
		 */
		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
				!capable(CAP_SETGID))
			return -EPERM;
	}

	/*
	 * We hope to recycle these flags after 2.6.26
	 */
	if (unlikely(clone_flags & CLONE_STOPPED)) {  
		static int __read_mostly count = 100;

		if (count > 0 && printk_ratelimit()) {
			char comm[TASK_COMM_LEN];

			count--;
			printk(KERN_INFO "fork(): process `%s' used deprecated "
					"clone flags 0x%lx\n",
				get_task_comm(comm, current),
				clone_flags & CLONE_STOPPED);
		}
	}

	/*
	 * When called from kernel_thread, don't do user tracing stuff.
	 */
	if (likely(user_mode(regs)))
		trace = tracehook_prepare_clone(clone_flags);

	p = copy_process(clone_flags, stack_start, regs, stack_size,
			 child_tidptr, NULL, trace);   //
	/*
	 * Do this prior waking up the new thread - the thread pointer
	 * might get invalid after that point, if the thread exits quickly.
	 */
	if (!IS_ERR(p)) {
		struct completion vfork;

		trace_sched_process_fork(current, p);

		nr = task_pid_vnr(p);

		if (clone_flags & CLONE_PARENT_SETTID)
			put_user(nr, parent_tidptr);

		if (clone_flags & CLONE_VFORK) {
			p->vfork_done = &vfork;
			init_completion(&vfork);
		}

		audit_finish_fork(p);
		tracehook_report_clone(regs, clone_flags, nr, p);

		/*
		 * We set PF_STARTING at creation in case tracing wants to
		 * use this to distinguish a fully live task from one that
		 * hasn't gotten to tracehook_report_clone() yet.  Now we
		 * clear it and set the child going.
		 */
		p->flags &= ~PF_STARTING;

		if (unlikely(clone_flags & CLONE_STOPPED)) {
			/*
			 * We'll start up with an immediate SIGSTOP.
			 */
			sigaddset(&p->pending.signal, SIGSTOP);
			set_tsk_thread_flag(p, TIF_SIGPENDING);
			__set_task_state(p, TASK_STOPPED);
		} else {
			wake_up_new_task(p, clone_flags);
		}

		tracehook_report_clone_complete(trace, regs,
						clone_flags, nr, p);

		if (clone_flags & CLONE_VFORK) {
			freezer_do_not_count();
			wait_for_completion(&vfork);
			freezer_count();
			tracehook_report_vfork_done(p, nr);
		}
	} else {
		nr = PTR_ERR(p);
	}
	return nr;
}

先了解do_fork的参数含义

unsigned long clone_flags 重点,该值中的每个位都代表对子进程task_struct中的每种属性的克隆设置
unsigned long stack_start:  子进程用户态堆栈的地址
struct pt_regs *regs: 指向pt_regs结构体的指针。当系统发生系统调用,即用户进程从用户态切换到内核态时,该结构体保存通用寄存器中的值,并被存放于内核态的堆栈中;
unsigned long stack_size:栈的大小
int __user *parent_tidptr:父进程在用户态下pid的地址
int __user *child_tidptr:子进程在用户态下pid的地址


关于clone_flags可以设置的值:

#define CSIGNAL     0x000000ff  /* signal mask to be sent at exit */
 #define CLONE_VM    0x00000100  /* set if VM shared between processes */
 #define CLONE_FS    0x00000200  /* set if fs info shared between processes */
 #define CLONE_FILES 0x00000400  /* set if open files shared between processes */
 #define CLONE_SIGHAND   0x00000800  /* set if signal handlers and blocked signals shared */
 #define CLONE_PTRACE    0x00002000  /* set if we want to let tracing continue on the child too */
 #define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release */
 #define CLONE_PARENT    0x00008000  /* set if we want to have the same parent as the cloner */
 #define CLONE_THREAD    0x00010000  /* Same thread group? */                                                                      
  #define CLONE_NEWNS 0x00020000  /* New namespace group? */
 #define CLONE_SYSVSEM   0x00040000  /* share system V SEM_UNDO semantics */
 #define CLONE_SETTLS    0x00080000  /* create a new TLS for the child */
 #define CLONE_PARENT_SETTID 0x00100000  /* set the TID in the parent */
 #define CLONE_CHILD_CLEARTID    0x00200000  /* clear the TID in the child */
 #define CLONE_DETACHED      0x00400000  /* Unused, ignored */
 #define CLONE_UNTRACED      0x00800000  /* set if the tracing process can't force CLONE_PTRACE on this clone */
 #define CLONE_CHILD_SETTID  0x01000000  /* set the TID in the child */
 #define CLONE_STOPPED       0x02000000  /* Start in stopped state */
 #define CLONE_NEWUTS        0x04000000  /* New utsname group? */
 #define CLONE_NEWIPC        0x08000000  /* New ipcs */
 #define CLONE_NEWUSER       0x10000000  /* New user namespace */
 #define CLONE_NEWPID        0x20000000  /* New pid namespace */
 #define CLONE_NEWNET        0x40000000  /* New network namespace */
 #define CLONE_IO        0x80000000  /* Clone io context */





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

五癫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值