在之前的进程状态一文中我们初步了解到了僵尸进程,我们都知道僵尸进程是一个已经运行完毕但然仍占用内存资源的进程,它的存在会浪费系统资源,我们必须想方设法将僵尸进程清理掉。
先来想一下为什么会存在僵尸进程,一个进程的回收是由它的父进程来进行检验回收的,但是当父进程正在处理自己的任务而无暇顾及子进程的情况时就会暂时搁置子进程,而如果此时子进程运行完毕,被搁置的状态就是僵尸状态。
因此一般我们需要让父进程进行等待操作,检验子进程的完成情况,能够将运行完毕的子进程快速回收。这个操作叫做进程等待。
Linux下给我们提供了两个系统调用,wait和waitpid,其中wait是waitpid的一个子集,相比之下我们更常用waitpid
wait:
参数只有一个输出型参数status,如果不关心status的结果,可以再调用wait时传入NULL
此系统调用为阻塞等待,即子进程运行完毕后才继续执行父进程
status:
status的二进制形式的低16位存储了退出码和信号信息,当我们想要通过status读取一个进程的退出码或信号时可以通过对status的位运算来获取
为了方便获取,Linux为我们提供了多个宏函数来辅助读取。
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
waitpid:
第一个参数为要等待的子进程id(-1标识所有子进程),第二参数为为输出型参数status,第三个参数为选项
当id=-1,option=0时waitpid就特化成了wait(阻塞等待),但是有时候我们想要让父进程等待子进程的同时能够处理自己的任务,我们就需要进行非阻塞等待,只需要把option参数改为WNOHANG即可
实例:👇
#include <stdio.h>
#include <unistd.h>
#include <sys/type.h>
#include <sys/wait.h>int main(){pid_t id=fork()//创建子进程if(id<0){perror("fork fail");exit(1);}else if(id==0){printf("I an child progress\n");sleep(3);}else{int status=0;printf("I am father progress\n");pid_t ret=waitpid(-1,&status,0);//等价于wait(&status)//pid_t ret=waitpid(-1,&status,WNOHANG); 非阻塞等待return 0;
}
//wait和waitpid的返回值为等待成功的子进程id
(waitpid如果等待失败立即出错返回,waitpid如果返回0则表示所等待的子进程仍处于运行状态未退出,父进程先处理自己的任务,进行下一次等待,所以如果我们进行非阻塞等待,通常会将waitpid放在一个循环体里)
(如果waitpid等待成功,并且第一个参数是-1,那么返回值是任意一个子进程的id)
进程程序替换:
绝大多数情况下父进程创建子进程的目的是让子进程处理与父进程不同的任务,那么就需要子进程有自己代码文件。
在子进程创建伊始,常量数据和代码段会和子进程共享,需要修改的数据会发生写时拷贝到自己的地址空间中,一旦进程替换,老旧的数据会被清理,被新的数据所替代。
因此可以确定的是进程替换不会产生新的进程,而只是改变了进程所对应的代码数据
Linux为我们提供了一execl系列的函数用于进行替换,具体使用可以参考标准文档。
实例:
int main(){printf("progress begin!\n");execl(…………);printf("progress end!\n");return 0;
}
运行上述代码片段,*progress end!*可能能打印,也可能不会打印,分别对印了进程替换成功和失败。
倘若进程替换成功,那么execl以后的代码数据就不再有任何意义了,因为此时进程对应的代码已经是别的代码。