Linux-进程创建-进程中止-进程等待

1. 进程创建

1.1 fork函数

  子进程拷贝父进程的PCB,子进程的大部分数据是来源于父进程,例如:内存指针(数据段和代码段)
  父进程创建子进程成功之后,父进程是独立的两个进程,父子进程的调度取决于操作系统内核,进程是抢占的方式执行的,父子进程谁先运行是不确定的,子进程拥有自己独立的进程。

1.2 写时拷贝

在这里插入图片描述
  一个进程就会有一个PCB,task_struct,父进程内存指针指向了父进程的进程虚拟地址空间,但是这个进程虚拟地址空间并不能真正的存储数据,真正的数据是存储在物理内存上的,因此,我们需要把进程虚拟地址空间和物理内存上的数据对应起来,我们就引入了页表。
  当父进程执行fork函数创建了一个子进程,子进程会复制父进程的内存指针,进程虚拟地址空间中的内容还有页表中的对应关系,并且页表中所对应的数据的也是一样的,但是当在子进程中如果修改了数据的值,这时就会存在写时拷贝会在物理内存中重新开辟一个空间

1.3 vfork函数

  使用vfork函数创建的出来的子进程和父进程指向同一个进程虚拟地址空间。

问题
  在进行函数调用压栈的时候,如果子进程调用不出栈,那么父进程也将无法出栈。

解决方案
  先把子进程处理完,再处理父进程。

补充: 执行ulimit -a,里面中有一个max user processes,这个是最大进程限制。
在这里插入图片描述

2. 进程终止

2.1 场景

  1. 从main函数的return返回
  2. 代码执行完毕获得目标结果
  3. 代码执行完毕未获得目标结果
  4. 程序崩溃,异常中止

2.2 终止的方法

  1. main函数return返回
  2. exit函数
  3. _exit函数

2.2.1 exit函数和_exit函数区别

  在linux的标准库函数中,有一套称作高级I/O的函数,我们熟知的printf 、fopen 、fread 、fwrite都在此列,他们也被称作缓冲I/O。其特征是对应每一个打开的文件,都存在一个缓冲区, 在内存中都有一片缓冲区,每次读文件会多读若干条记录,这样下次读文件时就可以直接从内存的缓存中取出,每次写文件时也仅仅是写入到内存的缓冲区,等待满足一定的条件(达到一定的数量,或者遇到特定字符,如换行和文件结束符EOF),再将缓冲区的内容一次性的写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦,如果有些数据,我们认为已经写入了文件,实际上因为没有满足特定的条件,他们还只是保存在缓冲区内,这时我们用_exit函数直接将程序关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用exit函数。

  1. exit是一个库函数,_exit是一个系统调用函数。
  2. exit函数定义在stdlib.h中,而_exit定义在unistd.h中。
  3. exit和_exit都是用于终止的函数,但是exit会执行用户定义的清理函数和刷新缓冲区和关闭流等。
    在这里插入图片描述

  exit是一个库函数,_exit是一个系统调用函数,而在C库中会冲刷缓冲区关闭流。
在这里插入图片描述
在这里插入图片描述
  void exit (int status)
  void _exit (int status)
  status会被进程等待接口获得,使用echo $?查看进程退出码

echo $?

在这里插入图片描述
  因为我在这里的程序是正常运行然后退出的,所以进程退出码是0。

2.3 执行用户定义的清理函数

int atexit (void(*function)(void))
{
	atexit(func);
	printf("hello\n");
}

  这里面的参数是一个指针函数,func是一个回调函数,当etexit退出时执行回调函数

2.4 刷新缓冲区的方式

  1. \n
  2. fflush函数
  3. exit()
  4. main函数的return

3. 进程等待

3.1 作用

  父进程进行进程等待,子进程先于父进程退出,由于父进程在等待子进程,所以父进程会回收子进程退出资源,从而防止子进程变成僵尸进程。

3.2 wait函数

头文件
  #include <sys/wait.h>

pid_t wait (int *status);
在这里插入图片描述
返回值
  成功  返回子进程pid
  失败  -1
接收的参数
  整型指针  int *  4个字节,但只是用最右边的两个字节。
在这里插入图片描述
  进程退出码是return ,exit(),exit()返回过来中间的数字。
  coredump标志位,如果为1,表示进程有coredump产生,核心转储文件,如果为0,表示当前进程没有coredump产生。
  终止信号,当前的程序是由什么信号导致终止的,比如有6号信号(double free)和11号信号(解引用空指针)。

正常情况
  只看进程退出码。
异常情况下
  进程退出码毫无意义,这时需要看终止信号和coredump标志位

补充
  参数int* status是一个出参,调用者准备一个int类型变量,将地址传递给wait函数,wait函数在自己内部进行实现赋值,当wait函数返回值之后,调用者就可以通过int变量,获取进程退出的信息。

3.3 查看进程调用堆栈的命令

命令: pstack [pid]
在这里插入图片描述

3.4 阻塞

  当前执行流调用某个函数的时候,一直没有返回,此时称为这个现象是阻塞。子进程退出会给父进程发送SIGCHLD信号,而父进程会忽略处理,wait内部就是判断是否接收到一个SIGCHLD信号,如果有,回收资源。

如何获取进程退出码?
  (status>>8)& 0xFF
如何获取终止信号?
  status & 0x7F
如何获取coredump?
  (status>>7) & 0x1

注意
  如果想要获取coredump,需要执行ulimit -a
在这里插入图片描述
命令: unlimit -c unlimited
功能: 接收coredump
命令: unlimit -c 0
功能: 不接收coredump

3.5 waitpid函数

pid_t waitpid(pid_t pid,int * status,int options)
返回值一共有四类,重点有这两个
  -1  等待任一子进程,一旦等待到了,则返回,比如多个子进程一个子进程返回则返回。
  >0  等待特定的子进程,大于0的值就是子进程PID号

options

  WNOHANG    非阻塞等待方式(非阻塞等待需要搭配循环,直到完成函数功能)
  0         阻塞等待

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天津 唐秙

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

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

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

打赏作者

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

抵扣说明:

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

余额充值