【Linux】系统调用fork创建子进程

系统调用创建进程

  • 指令man 2 fork 调用系统手册认识fork
  • fork();函数创建一个子进程
  • 头文件#include<unistd.h>

下面使用代码创建一个子进程demo:

int main()
{
        printf("fork before process id : %d\n", getpid());
        fork();
        printf("fork after process id : %d\n", getpid());
        return 0;
}

  • fork()之后,变成了两个执行流
  • fork()函数创建成功,pid返回的是子进程pid的是父进程,pid返回的是0的是子进程。如果返回-1,父进程(也就是准备创建的进程)创建失败
int main()
{
        printf("pid: %d---ppid: %d \n", getpid(), getppid());
        pid_t id = fork();
        if(id == 0)
        {
                // 子进程
                while(true)
                {
                        printf("child process: pid-%d-ppid-%d\n",getpid(),getppid());
                        sleep(1);
                }       
        }
        else if(id > 0)
        {
                // 父进程
                while(true)
                {
                        printf("father process: pid-%d-ppid-%d\n",getpid(),getppid());
                        sleep(1);
                }
        }
        else
        {
                // 创建进程失败
                printf("create fail: pid-%d-ppid-%d\n",getpid(),getppid());
        }
        return 0;
}

  • 监控脚本:while :; do ps axj | head -1 && ps axj | grep process | grep -v grep ; sleep 1;done
  • 子进程的ppid和父进程的pid是相同的
  • 可以通过指令创建子进程,也可以通过系统调用fork()创建一个子进程

fork——翻译成中文是分支的意思,fork()之后会变成两个执行流。


问题1:为什么fork要给子进程返回0,给父进程返回子进程pid?

        【答】:返回不同的返回值,是为了区分让不同的执行流,执行不同的代码块。一般而言,fork之后的代码父子进程会共享。类似于树的数据结构,父进程可以有多个子进程,每一个子进程都会有自己的pid,fork之后获取对应子进程的pid,是为了父进程找到子进程;而子进程只有唯一的父进程,只需要调用getppid就可以找到自己的父进程。


问题2:一个函数是如何返回两次的?如何理解?

        【答】:fork也是一个函数,fork本身一定是在操作系统中实现的。在fork函数里面,操作系统已经创建了新的子进程,而进行到函数的返回值的时候,已经存在两个进程了,也就是说两个进程同时执行了两次return语句。


问题3:一个变量id怎么会有两个不同的内容?

        【答】:子进程在创建出来的时候,同样也获得了父进程的代码和数据,因为进程具有独立性,而子进程的数据是有可能会被修改的,所以不能让父进程和子进程共享同一份数据,这里需要说明共享代码和数据并不会影响独立性。理论上来讲,子进程在被创建出来的时候,会拷贝一份父进程的数据,由于父子进程各自独立,当子进程修改数据后,并不会对父进程产生影响。

        但是在实际中,为了减少消耗,操作系统让子进程指向父进程的数据,如果子进程需要读取父进程的数据,直接在父进程的数据区读取即可,而当子进程需要修改父进程的数据(独立性)时,操作系统会在内存中重新开辟一块新的区域,去保存这个修改了的数据。这种技术叫做数据层面的写时拷贝

在任何平台,进程在运行的时候,是具有独立性质的。

        fork()函数return语句在执行的时候,就是发生了写时拷贝,也就是获得了两个不同的返回值。


问题4:fork()函数具体做了什么?

        【答】:进程 = 内核数据结构 + 代码和数据;fork之后代码是共享的,当代码加载到内存的时候,代码是不允许被修改的,只能修改的是代码的数据。

        同样的代码,为什么还需要创建子进程,让子进程和父进程完成相同的代码呢?这是为了让父进程和子进程执行不同的内容。所以需要父和子进程执行不同的代码块,此时就需要fork函数返回不同的返回值,根据返回值的不同,进行不同的执行流。

        而fork的本质就是创建一个新的进程,在内存中创建新的PCB,修改PCB(task_struct)的部分属性,让这个task_struct执行父进程的代码和数据。如果不想让父子进程执行相同的执行流,就必须根据fork函数的返回值来选择执行。


问题5:fork创建两个进程后,父子进程谁先运行呢?

        【答】:一般而言,一旦一个进程被创建出来,谁先运行是由调度器来决定的。

调度器:对特定数据结构进行查找的一种算法,调度器一般都是公平的,尽可能保持每一个进程的时间片相同。


代码中父进程的父进程

代码中可以通过fork函数创建子进程,而这个代码也是一个进程,这个进程的父进程是bash进程。而这个bash进程在创建子进程的时候,也一定会使用fork这个函数,创建完进程后,父进程执行命令行解释器,子进程执行解析新的命令。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值