进程控制

一、进程创建

首先,我们需要知道,创建一个进程,就相当于创建一个PCB,因为我们知道在linux操作系统下,PCB就是一个task_struct结构体,这个结构体存放在内核中,只能通过系统调用接口实现创建。

1.1 相关接口

接口:pid_t fork(void);
在这里插入图片描述

功能:创建一个子进程,父子进程各有各的虚拟地址空间,具有写时拷贝技术。(代码共享数据独有)
返回值:对于父进程来说,返回的是子进程的pid(大于0),对于子进程来说,返回的是0,如果创建子进程失败,则返回负数。
接口:pid_t vfork(void);
在这里插入图片描述

功能:创建一个子进程,父子进程共用父进程的虚拟地址空间,并且子进程先运行,直到子进程退出或者程序替换之后,父进程才能运行。
返回值:与fork相同
接口:getpid();
功能:获取当前进程的pid。
演示
在这里插入图片描述
这里可以看出fork和vfork的区别,对于fork而言,父子进程的运行先后顺序是随机的,而对于vfork而言,只有在子进程运行完毕或者进程替换后,父进程才可以运行。

1.2 相关技术与性质

1.写时拷贝技术(fork):子进程创建后与父进程映射在同一块物理内存,当物理内存中的数据即将发生改变时,操作系统重新为子进程开辟物理内存,拷贝数据。(避免直接给子进程开辟空间却不使用,降低了进程创建效率,造成内存数据冗余)
2.进程具有独立性。(父子进程各有各的虚拟地址空间,映射各自的数据----fork)
3.进程之间不会出现交叉关系,不会受到其他进程的影响(可以保障进程的稳定运行)。

二、进程终止

对于我们而言,我们最熟悉的就是return,但是对于普通函数(除了main函数)而言,return只能去退出函数,并不能退出进程,所以在这里我需要介绍两个退出进程的接口

2.1 exit

接口:void exit(int status)
在这里插入图片描述
功能:可以在程序任意的位置使用,去退出一个进程。

2.2 _exit

接口:void exit(int status)
在这里插入图片描述
功能:可以在程序任意的位置使用,去退出一个进程。

2.3 exit和_exit的区别

上面关于exit和_exit大家会发现我写的功能相同,但是两个接口是有本质的区别的,在这里我们详细去说明
首先,我们看个实例:
在这里插入图片描述

当我们使用exit和_exit去退出进程时,我们发现,使用_exit接口去退出进程时,fun函数中打印"*********”字符串并没有显示,而当我给字符串进行刷新缓冲区数据("\n"—刷新标准输出缓冲区)时,最终进程退出就会去打印"*****”字符串,所以得到区别:
exit在退出进程时,会刷新缓冲区,将缓冲区中的数据写入文件,而_exit会直接退出进程,释放资源,不会刷新缓冲区。
正常退出:使用return、exit、_exit退出的进程
异常退出:程序出现异常,导致中途退出

三、进程等待

进程等待的目的就是父进程等待子进程退出,获取退出子进程返回值,释放退出子进程的所有资源,避免僵尸进程。

3.1 wait

接口:int wait(int * status);
在这里插入图片描述

功能:处理退出的子进程,如果调用该接口时并没有子进程退出,则会使得父进程阻塞(完成某个功能,发起某个某个调用,如果当前不具备完成此功能的条件,则调用等待),直到子进程退出。
返回值:成功返回退出子进程的pid,失败返回-1
int * status:输出型参数,用于获取退出子进程的返回值。
举个例子
在这里插入图片描述
根据例子就可以看出,子进程无论是否正常退出,父进程没有去关注子进程的退出(没有去获取子进程退出的返回值),就会造成子进程为僵尸进程.使用wait父进程就会一直等待任意子进程退出,获取子进程的返回值,去释放子进程所有资源。

3.2 waitpid

接口:int waitpid(int pid,int * status,int option);
在这里插入图片描述
功能:与wait类似,但它可以等待指定的。
int pid:指定子进程的pid,如果等待任意子进程则设置为-1。
int * status:输出型参数,用于获取退出子进程的返回值。
int option:设置阻塞或者非阻塞,阻塞为0,非阻塞为WNOHANG。
返回值:成功返回退出子进程的pid,没有子进程退出返回0,失败返回-1。
举个例子
阻塞条件下:
在这里插入图片描述
在阻塞条件下,父进程会一直去等待指定的子进程,如没有指定的子进程退出,则一直等待,当指定子进程退出,父进程就会去获取该子进程的退出返回值,从而去释放子进程所以资源,避免子进程僵尸状态。
非阻塞条件下:
在这里插入图片描述
这里我们发现,非阻塞条件也会造成子进程僵尸状态,这时我么那就需要去直到,要进行非阻塞操作,就必须要去循环处理,没有等待到子进程退出,可以去干其他的事情,当子进程退出时就去获取子进程的退出返回值。如图:
在这里插入图片描述

3.3 退出子进程的返回值-status

通过等待接口中int status获取的子进程的返回值,并且返回值只使用一个字节保存,正常退出的返回值存储在低16位的高8位,异常退出存储在低7位(第八位为coredump(核心转储–程序异常退出,将退出前的运行信息保存下来(默认是关闭的))标志位),如果正常退出,低8位为0。
举个例子:
在这里插入图片描述
从这个例子可以看出,子进程返回了5,存储在了status的低16位中的高8位,从而打印了1280。

四、进程替换

什么是进程替换?
顾名思义,进程替换就是去替换一个进程正在调度运行的程序,也就是将PCB指针指向新的程序。
为什么要有进程替换?
我们创建子进程的目的并不是让子进程和父进程干相同的事情,而是让子进程运行调度新的程序,这就是进程替换。

4.1 exec函数族

在这里插入图片描述
const char * path:新的运行程序的绝对路径。(不带路径,则在当前路径查找)
const char * file:新的运行程序的相对路径。(不带路径则在PATH环境变量路径下查找)
const char *arg,…: 程序运行参数,逐个赋予,最后以NULL结尾。
char *const argv[]:程序运行参数字符串指针。
char *const envp[]:环境变量
举个例子:
execl:
在这里插入图片描述
execv:
在这里插入图片描述
关于进程替换的演示就到这里,大家也可以自行去验证其他的接口,方法类似!

4.2 区别

execl和execv的区别:运行参数是字符串还是字符串指针数组
加上p(execlp和execvp):第一个参数为相对路径,如果不带路径则在PATH环境变量路径下查找
加上pe(execle和execvpe):增加环境变量,可以自己设置环境变量。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值