Linux下的进程控制

Linux下的进程控制

1、进程创建

Linux下采用fork创建一个新进程,新进程为原来进程的子进程

#include <unistd.h>
pid_t fork(void);
返回值,出错返回-1,父进程返回子进程的ID,子进程返回0

当一个进程调用fork以后,就会有两个二进制代码相同的进程,自此之后,父子进程分别执行各自的功能。

在这里插入图片描述
在这里插入图片描述

从如上的程序运行来看,当程序fork以后,自此之后的程序各自运行各自的程序,而不会打印两次before的内容注意,至于是两个进程谁先运行时取决于谁先拿到时间片谁先运行

写时拷贝技术:

通常情况下父子进程共享代码,当数据不发生改变时,父子数据也是共享的,但是当有父进程或者子进程修改了数据,这个时候,父子进程就会分出两个数据块,用来拷贝修改的数据,这样就大大的提高了创建进程的效率,使得fork成为最常用的创建进程的方式。

在这里插入图片描述
在这里插入图片描述
fork常规的用法:

1、一个父进程希望复制自己,使得父子进程同时执行不同的代码段。
2、一个进程要执行不同的程序。

fork常见的创建进程失败的原因:

系统中的进程过多,导致用户的进程数超出了限制

2、进程退出

为什么要退出一个进程

1、进程异常终止
2、进程运行完毕
3、释放资源

进程的退出方式:

1、main函数中的return
2、库函数接口exit()
3、系统调用接口_exit()
4、(异常退出)ctrl+c

exit()函数:

#include <unistd.h>
void exit(int status);

退出前刷新缓冲区
可以在任意地方退出进程

在这里插入图片描述

在这里插入图片描述

_exit()函数:

#include <unistd.h>
void _exit(int status);

不会刷新缓冲区
可以在任意位置退出进程
在这里插入图片描述

在这里插入图片描述

main函数中return

退出前会刷新缓冲区
只有在main函数中return才会退出一个进程
注:main函数中return的那个值会作为exit中的参数status。

在这里插入图片描述
在这里插入图片描述

3、进程等待

若没有进程等待,子进程先于父进程退出,就可能出现僵尸进程,会造成内存泄漏。
父进程可以采用进程等待的方式等待子进程退出,并获取子进程的退出信息,并回收子进程资源

wait()函数:

#include <sys/wait.h>
pid_t wait(int* status);

参数:进程的退出状态,不关心可置为NULL
返回值:成功返回pid 失败返回-1

waitpid()函数:

#include <sys/wait.h>
waitpid(pid_t pid, int* status, int options);

参数:

参数描述
pid_t pid = -1代表等待任意一个进程
statusWIFEXITED(status)若为正常终止的状态,则为真(查看进程是否正常退出)WEXITSTATUS(status)若WIFEXITED(status)非零,提取进程的退出码(查看进程的退出码)
optionsWNOHANG若pid指定的进程没有结束,则waitpid函数返回0,正常结束,返回进程的pid

返回值:正常退出,返回pid,进程没有结束返回0

注意:

若是子进程已经退出调用wait/waitpid时,wait/waitpid立即返回,并且释放资源获取子进程的信息
若是在任意时刻调用,子进程可能阻塞
若是不存在子进程,则立即报错返回

获取子进程的status
wait和waitpid会将子进程的status填充进去
如果传递NULL代表不关心子进程的status

status中低16位中的高八位表示进程的退出状态
status中低16位中的低八位中第八位是core dump标志,剩下的低七位表示进程的终止信号,即被信号杀死的进程的信号。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
进程的阻塞等待实现:
在这里插入图片描述
在这里插入图片描述
进程的非阻塞等待实现:
在这里插入图片描述
在这里插入图片描述

4、程序替换

程序替换的原理
在这里插入图片描述

用fork创建进程后,子进程执行的是和父进程相同的程序,子进程常需要调用exec函数族中的函数来执行另外一个程序,当进程调用exec函数族中的函数时,该进程的用户空间代码和数据完全被新程序替换。但是进程的ID不会改变,也就是说,程序替换替换的是进程PCB映射在物理内存上的数据段和代码段

替换函数:

#include <unistd.h>
int execl(const char* path, const char* arg, …);
int execlp(const char* file, const char* arg, …);
int execle(const char* path, const char* arg, …, char* const envp[]);
int execv(const char* path, char* const arg[]);
int execvp(const char* file, char* const arg[]);
int execve(const char* path, char* const arg[], char* const envp[]);

在这里插入图片描述

这些函数调用成功则加载新的程序,不再返回
出错返回-1
exec函数只有出错返回没有成功返回值

字母描述
l(list)表示参数采用列表的形式
v(vector)表示参数采用数组的形式
p(path)表示自动搜索环境变量path
e(env)表示自己指定环境变量

区别如下:

函数名称参数格式是否带路径是否使用当前环境变量
execl列表
execlp列表
execlv列表
execv数组
execvp数组
execve数组

只有execve是系统调用接口,其他的都是库函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

总结:

一个程序可以通过调用函数来完成特定的功能,这是程序内部进行功能封装的方式
Linux系统鼓励将此方式运用到进程之间,就有了fork exec进行程序替换,wait/waitpid来进行程序等待获取进程退出的status,最后通过exit/_exit/main函数中的return返回。以此来达到进程之间的替换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值