2.一个奇怪的fork程序

进程起源:

         道生一(start_kernel,内核启动函数),一生二(kernel_init和kthreadd),二生三(即前面0、1和2三个进程),三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先)。

 

fork.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc,char * argv[])
{
    int pid;
    /* fork another process */
    pid = fork();       // fork是用于创建子进程的系统调用
    if (pid < 0)
    {
        /* error occurred */
        fprintf(stderr,"ForkFailed!");
        exit(-1);
    }
    else if (pid == 0)
    {
        /* child process */
        printf("This is ChildProcess!\n");
    }
    else
    { 
        /* parent process  */
        printf("This is ParentProcess!\n");
        /* parent will wait for the child tocomplete*/
        wait(NULL);
        printf("Child Complete!\n");
    }
}


         编译、运行fork.c:

cgh@ubuntu:~/test$gcc fork.c       #编译
cgh@ubuntu:~/test$./a.out                    #运行
This is ParentProcess!
This is ChildProcess!
Child Complete!


 

fork.c中有一个if else的条件判断语句,main函数执行时if else中只能有一个满足执行条件。

1.      当pid < 0时屏幕打印"Fork Failed!"。

2.      当pid == 0时屏幕打印"This is Child Process!\n"。

3.      其他情况屏幕打印"This is Parent Process!\n"和"ChildComplete!\n"

正常情况下只能打印以上三种情况中的一种,然而运行fork.c时屏幕上同时打印了2、3两种情况。

fork.c打破了if else条件分支的结构?当然不是,fork系统调用在父进程和子进程各返回一次。Fork之后这段代码同时在两个进程中执行了。

在子进程中fork()的返回值是0,pid = 0,执行了else if中的代码。在父进程中fork()的返回值是子进程的pid(> 0),执行了else中的代码。

 

 

接下来分析创建一个新进程在内核中的执行过程(下面的看不懂可以跳过,第三讲有详细分析

fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork这个内核函数来实现进程创建的;

Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:

复制一个PCB——task_struct

1.  err = arch_dup_task_struct(tsk, orig);

 

要给新进程分配一个新的内核堆栈

1.  ti = alloc_thread_info_node(tsk, node);

2.  tsk->stack = ti;

3.  setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈

 

要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部。

从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?在copy_process中的copy_thread设定。

1.  *childregs = *current_pt_regs(); //复制内核堆栈

2.  childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!

3.  p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶

4.  p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值