💻文章目录
- 📄前言
- 进程等待
- 进程等待的概念
- 进程等待的方法
- 进程替换
- 进程替换的概念
- 替换方式
- 📓总结
📄前言
在如今的时代,多进程编程已经变成了必不可少的一部分,而进程等待、进程替换这两个概念都是作为多进程编程所必不可少的知识,为了掌握多进程编程,今天就从进程等待与替换开始吧。
进程等待
进程等待的概念
我们知道linux中进程拥有多种状态,例如僵尸状态、阻塞状态、运行状态等,而其中僵尸状态就是因为父进程没有接受到子进程的退出码而导致的状态,僵尸状态意味着系统无法对进程pcb的资源进行回收,过多的僵尸程序将导致系统内存泄漏、反应卡顿,严重时甚至会让系统挂死。所以,一个正常的多进程程序,父进程必须要接收完所有子进程的退出码,而这个接收的过程就是进程等待 <T_T。
进程等待的方法
进程等待的方式分为两种:
- 阻塞等待:如同字面的意思,在等待子进程退出码的时候停止(阻塞)父进程的运行。
- 非阻塞等待:在子进程运行的同时,父进程也运行代码,但是得不时对子进程的退出情况进行检查。
在C语言中,等待进程的函数为 wait 和 waitpid,它们的差别在与参数的不同。
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
/*
返回值:
成功返回被等待进程 pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
*/
pid_ t waitpid(pid_t pid, int *status, int options);
/*
返回值:
正常返回时返回子进程的pid,如果调用失败返回-1;
注意:如果options设置成了非阻塞等待,子进程没有退出时返回0;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效;
Pid>0.等待其进程ID与pid相等的子进程;
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出);
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码);
options:
WNOHANG: 若pid指定的子进程没有结束,程序返回0,父进程继续执行代码,在一段时间再次调用确定子进程退出;
*/
- 阻塞等待
阻塞等待意味着父进程在等待期间是什么都不做的,所以期间只有子进程在运行。
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork(); //在linux系统中使用fork来生成子进程
if(id < 0)
_exit(1); //子进程生成异常
if(id == 0) //分流
{ //子进程
printf("child is run, pid is:%d\n",getpid());
sleep(3);
exit(0);
}
else
{ //父进程
int status = 0;
pid_t ret = waitpid(-1, &status, 0); //阻塞等待
if(WIFEXITED(status) && ret == id)
printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
else
{
printf("wait child failed, return.\n");
return 1;
}
}
return 0;
}
- 非阻塞等待
非阻塞等待只需把 waitpid 最后的参数改为WNOHANG即可
int main()
{
pid_t id = fork(); //在linux系统中使用fork来生成子进程
if(id < 0)
_exit(1); //子进程生成异常
if(id == 0) //分流
{ //子进程
printf("child is run, pid is:%d\n",getpid());
sleep(3);
exit(0);
}
else
{ //父进程
int status = 0;
pid_t ret;
do
{
ret = waitpid(-1, &status, WNOHANG); //非阻塞等待
if (ret == 0)
{
printf("child is running\n");
}
sleep(1);
}while (ret == 0);
if(WIFEXITED(status) && ret == id)
printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
else
{
printf("wait child failed, return.\n");
return 1;
}
}
return 0;
}
进程替换
进程替换的概念
进程替代是指程序在运行期间执行另一个程序,但这个替换是把整个程序从头到尾都换成了新的程序(pcb还是同样的,所以进程id不变),即使替换的程序结束完毕,也不会回到原有的代码继续执行,所以一般都是由子进程来进行进程替换。
替换方式
C语言的进程替换函数
/*函数如果执行成功将会替换成新的程序,如果执行失败则返回-1*/
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 argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
虽然函数很多,但其实它们都有着一套很好记得命名规范:
- l (list):指使用列表参数,即可变参数列表(arg…)
- v(vector):指使用使用数组来传递参数
- p(path):指自动搜寻环境变量
- e(env):指自己维护环境变量
替换实例
int main()
{
pid_t id = fork();
if(id == 0)
{ //子进程
execlp("exa", "exa", "--icons", NULL); //p代表自动搜索环境变量,无需使用绝对路径
printf("child is exit\n"); //如果execlp执行成功不会打印
exit(1);
}
else
{ //父进程
printf("i'am father, pid:%d\n", getpid());
}
return 0;
}
📓总结
📜博客主页:主页
📫我的专栏:C++
📱我的github:github