进程等待&进程程序替换
- 进程等待
- 进程程序替换
- 通过进程等待和进程程序替换来理解守护进程
进程等待
僵尸进程的产生原因是:子进程先于父进程退出,在子进程退出时会给父进程发送SIGCHILD信号,而父进程接收到这个信号后选择不处理,从而导致子进程的退出状态信息没有被接收,从而导致子进程成为僵尸进程。
那么正是由于这个原因,我们就需要进行进程等待,来回收子进程的退出状态信息,防止子进程成为僵尸进程。
wait函数就可以实现这个功能
pid_t wait(int* status);
这个函数是一个出参类型的函数,需要我们传入status地址,让wait帮我们将其内部的值进行设置,从而保存退出状态信息
实际上,status只用了后两个字节,也就是低位的两个字节来保存退出状态信息。
这个函数是一个阻塞类型的函数,也就是说,当wait函数没有接收到子进程退出的退出状态信息,那么程序会一直停到这行代码,直到等到接收到了退出状态信息,才会继续向下执行。
使用status得到退出状态信息的方法,按位与。
当子进程正常退出,如果传递了status的地址,会得到子进程的退出码
当子进程非正常退出,如果传递了status的地址,会得到子进程的core dump标志位和退出信号。
注意:coredump标志位表示的是子进程如果异常退出了,coredump值为1,表示产生了核心转储文件,0表示没有产生,是否产生核心转储文件不是单纯取决于进程异常退出,而是取决于。
例如当前的代码,子进程会异常退出,但是没有产生coredump文件。
可以看到coredump值为0,没有产生核心转储文件,使用ulimit -c
查看当前coredump的大小,可以看到大小为0,意味着即使产生了,其大小也为0,所以coredump标志位的值为0。
通过ulimit -c unlimited
将大小设为无限
这样就可以产生核心转储文件了。
除了使用wait函数之外,也可以使用waitpid来进行进程等待
pid_t waitpid(pid_t pid, int* status, int options);
pid | status | options |
---|---|---|
-1:等待任一子进程 | 子进程的退出状态信息 | WNOHANG:设置为非阻塞,子进程未结束返回0,结束范围进程pid |
0:等待指定pid的进程 |
需要注意的是:对于非阻塞类型的函数,需要搭配循环来使用
进程程序替换
对于一个bash进程,我们可以通过命令来运行自己的代码,bash进程是当前运行进程的父进程,通过进程概念的理解,我们自己的进程是拷贝了bash的代码的,那么为什么我们的进程不会运行bash进程呢?
原因就是进行了进程程序替换。
通过exec函数簇进行进程程序替换。
其中包含
//path需要带路径,arg第一个参数为可执行程序本身,多个参数用”,“隔开,结尾用NULL,
//若调用成功,则运行替换后的程序,调用失败返回-1
int execl(const char* path, const char* arg,...);
//与上述相同,path不用带路径
int execlp(const char* file, const char* arg,...);
//与上述相同,但是需要自己组织环境变量
int execle(const char* path, const char* arg,..., char* const envp[]);
//与execl相似,但是传递的命令行参数是以数组指针的方式
int execv(const char* path, char* const argv[]);
//与execlp相似,但是传递的命令行参数是以指针数组的方式
int execvp(const char* fail, char* const argv[]);
//与execlp相似,但是传递的命令行参数是以指针数组的方式
int execve(const char* path, char* const argv[], char* const envp[]);
总结起来就是:观察函数尾部带的符号来判断需要传递的参数是什么方式
函数名中带l的 | 函数名中带v的 | 函数名中带p的 | 函数名中带e的 |
---|---|---|---|
表示传递的参数为可变参数列表 | 表示传递的参数是以数组指针的方式 | 可以使用PATH,无需写全路径 | 需要程序员自己设定环境变量 |
值得注意的是,除了execve之外,其他的exec函数都是库函数
这里使用execl演示
运行后的结果
通过进程等待和进程程序替换来理解守护进程
当某个进程运行的时候,我们需要进程进行守护
创建一个守护进程,让守护进程和被守护的进程进行进程间通信(可以选择共享内存或者消息队列),让被守护的进程每秒发送一个时间,让守护进行读,当守护进程发现前一秒的时间和当前时间相等时,意味着被守护的进程出现了问题,那么就kill掉这个进程,由守护进程创建出一个子进程,使用进程程序替换的方式将当前的子进程替换为要守护的进程,从而实现进程的守护。