fork函数,进程等待,进程终止,写时拷贝

发布于:2022-12-28 ⋅ 阅读:(150) ⋅ 点赞:(0)

1.fork函数介绍

fork之前父进程独立执行,只有父进程没有子进程,fork之后,父子进程二个执行流分别执行,也就是说fork之后就有二个进程
进程具有独立性,代码和数据必须独立的,代码只能被读取,因为会产生写时拷贝
除了这些fork之后代码共享,那么fork之后,是不是只有fork之后的代码是被父子进程共享的,答案不是:fork之后,父子共享所有代码

但是子进程执行的后续代码 != 共享的所有代码,只不过子进程只能从这里开始执行

2.fork之后,操作系统做了什么?

首先我们知道
进程 = 内核的进程数据结构 + 进程的代码和数据

创建子进程的内核数据(struct task_struct + struct mm_struct + 页表 )+ 代码继承父进程,数据以写时拷贝的方式,来进行共享
这样子完成了独立性,因为这种办法,哪怕时子进程崩了,父进程也能单独运行

3.写时拷贝(深浅拷贝)

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本
写时拷贝本身就有操作系统完成的

在这里插入图片描述

为什么要写时拷贝?

我们可以创建子进程的时候,就把数据分开,不行吗?
答案是可以但是
1.父进程的数据,子进程不一定全用,即使全用,也不一定全部写入,所以会有浪费空间的可能
2. 最理想的情况,只有会被父子修改的数据,进行分离拷贝,不需要修改的共享,但是从技术角度复杂
3. 如果fork的时候,就无脑拷贝数据给子进程,无疑会增加fork的成本(内存和时间)

4.fork失败的原因

1.系统有太多进程
2.实际用户的进程超过了限制

模拟进程创建失败代码

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
  for(;;)
  {
    pid_t id = fork();
    if(id < 0)
    {
      printf("创建子进程失败....\n");
      break;
    }
    if(id == 0)
    {
      printf("I am a chile ... %d\n",getpid());
      sleep(2);
      exit(0);
    }
  }

  return 0;
}

创建失败图片
在这里插入图片描述

5.进程终止

为什么return 0,其他可以吗?给谁return
常见进程退出
1.代码跑完,结果正确
2.代码跑完,结果不正确
3.代码没跑完,程序异常了

那么return0;表达的意思是代码跑完结果正确,returrn非零表示失败,这些return的值表示进程退出码
那么为什么用非零来表示失败,不能表示正确吗?
因为:非零表示不同的结果,失败有很多原因,但是成功的结果只有一个
而至于这个return的值是给父进程读取,让父进程知道这个进程运行成功或者失败

证明代码

int main()
{
  return 123;
}

echo $? 这个命令,在bash中,最近一次执行完毕时,对应进程的退出码

在这里插入图片描述

那么再看一下下面结果
在这里插入图片描述

可以看到命令行退出码有特别的设定
那么我们1怎么知道命令退出码的意思
代码

int main()
{
  int i = 0;
  for(i=0;i<100;i++)
  {
    printf("%d; %s\n",i,strerror(i));
  }

  return 0;
}

在这里插入图片描述

结果
进程错误码显示不完全, 太多了

关于终止常见做法

1.在mian函数return
2.在自己的代码任意位置输入exit()
除了exit还有一个_exit,exit是终止进程并刷新缓冲区,_exit是终止进程,但不会刷新缓冲区

6.进程等待

1.为什么要进行进程等待

因为子进程退出,父进程如果不管不顾,就有可能造成僵尸进程的问题,进而造成内存泄漏
另外,进程一旦变成僵尸进程就无法被kill -9 杀死了

进程退出是有退出码的,而子进程return的进程码要被父进程给接受到,要不然父进程不知道子进程完成的怎么样

2.模拟进程等待

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  pid_t id = fork();
  if(id == 0 )
  {
    while(1)
    {
      printf("我是子进程我正在运行...pid: %d\n",getpid());
      sleep(1);
    }
  }
  else
  {
    printf("我是父进程: pid: %d, 我准备等待子进程\n",getpid());
    sleep(40);
    pid_t ret = wait(NULL);
    if(ret < 0)
    {
      printf("等待失败!\n");
    }
    else
    {
      printf("等待成功: result: %d\n",ret);
    }
    sleep(20);
  }

  return 0;
}

监测脚本

while :;  do ps axj | head -1 && ps axj | grep myproc | grep -v grep; sleep 1; echo "####################"; done

上面代码的意思是
先打印父进程,然后到子进程运行,然后我们手动在40秒把子进程给杀了,那么子进程就是z僵尸状态,然后过了40秒后调用了wait函数把子进程给回收了
那么子进程就消失了

下面图片已经是杀掉了子进程,子进程从S状态变成了Z状态
在这里插入图片描述
父进程回收了子进程所以子进程消失了
在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到