进程-3:fork()函数

1. 示例程序

#include <unistd.h>; 
#include <sys/types.h>; 
main () 
{ 
     pid_t pid; 
      pid=fork(); 
      if (pid < 0) 
              printf("error in fork!"); 
       else if (pid == 0) 
              printf("i am the child process, my process id is %dn",getpid()); 
        else 
               printf("i am the parent process, my process id is %dn",getpid()); 
} 

结果是:

i am the child process, my process id is 2139
i am the parent process, my process id is 2138

之前看过很多解释,觉得一次调用fork()函数,但返回两次,很神奇。下面我们来看看fork()函数源代码,你就会豁然大明白了。

2. fork() 源代码

fork()是一个系统调用,内核中sys_fork是其实现:

_sys_fork:    
        call _find_empty_process
	testl %eax,%eax
	js 1f
	push %gs
	pushl %esi
	pushl %edi
	pushl %ebp
	pushl %eax
	call _copy_process
	addl $20,%esp
1:	ret
这个函数先调用find_empty_process()函数,之后调用copy_process函数

I. 在进程一文中讲到进程一定要有一个task_struct结构,find_empty_process()函数就是在全局结构数组task中找了一个空的task_struct结构和一个未使用的进程id。返回任务号,即在数组中的位置。下面是其代码:

int find_empty_process(void)
{
	int i;

	repeat:
		if ((++last_pid)<0) last_pid=1;
		for(i=0 ; i<NR_TASKS ; i++)
			if (task[i] && task[i]->pid == last_pid) goto repeat;
	for(i=1 ; i<NR_TASKS ; i++)
		if (!task[i])
			return i;
	return -EAGAIN;
}

II. copy_process()函数 核心代码如下所示

int copy_process()
{
	struct task_struct *p;
	int i;
	struct file *f;

	p = (struct task_struct *) get_free_page();
	if (!p)
		return -EAGAIN;
	task[nr] = p;
	*p = *current;	/* NOTE! this doesn't copy the supervisor stack */
	p->state = TASK_UNINTERRUPTIBLE;
	p->pid = last_pid;
	p->father = current->pid;
	p->tss.esp0 = PAGE_SIZE + (long) p;
	p->tss.ss0 = 0x10;
    p->tss.eax = 0;
    p->tss.eip = eip;
	p->tss.cs = cs & 0xffff;
	p->tss.ss = ss & 0xffff;
	p->tss.ds = ds & 0xffff;
	set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
	set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
	p->state = TASK_RUNNING;	/* do this last, just in case */
	return last_pid;
}
第7行:先在内核空间中申请一页内存

第10行:把此页的地址赋给全局数组中相应的位置,也就是find_empty_process函数的返回值。

第11行:把当前的任务数据结构复制到刚申请的内存p处。

第12-16行:给新进程结构做一些修改,如:状态,进程id,父进程,内核栈等等

第17行:eax赋值为0,这个0就是新进程(子进程)fork的返回值。

第18-19行:关键的两行,cs:eip在当前进程指向的fork()的下一个指令:即示例程序的if(pid < 0)。当前进程将要执行此代码。而把cs:eip指向的代码赋值给新进程。因此新进程下一条指令也是if(pid < 0). 分别由两个进程从if(pid < 0)分别向下执行,因此执行到最后有两次返回。我说的清楚吗?

第20-21行:设置cs,ss,ds等代码段,堆栈段和数据段。

第22-23行:把task_struct中的tss结构与gdt相联系。在进程切换的时候,要保持原任务的上下文,也就是保存到了tss中的结构中。

第24行:设置新进程的状态为running

第25行:返回新进程的进程id,此值也是当前进程(父进程)fork对应的返回值,为子进程的进程id。

总的来说fork()做的工作就是创建了一个新的task_struct结构,对新进程结构做了一些修改。关键的就是对cs:eip的复制,让新老进程都共同指向了if(pid < 0) 指令,靠进程切换,分别向下执行,返回两次。





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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值