Linux——子进程回收

  • exec函数族      

        fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

        将当前进程的.text、.data替换为所要加载的程序的.text、.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变,换核不换壳。

        其实有六种以exec开头的函数,掌握前两个函数,统称exec函数:

        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[]);

     值得注意的是,exec函数一旦调用成功即执行新的程序,不返回只有失败才返回,错误值-1。所以通常我们直接在exec函数调用后直接调用perror()和exit(),无需if判断。

       l (list)                           命令行参数列表

       p (path)                       搜素file时使用path变量

       v (vector)                    使用命令行参数数组

       e (environment)       使用环境变量数组,不使用进程原有的环境变量,设置新加载程序运行的环境变量

   execlp函数(l是list的缩写,p是PATH的缩写)

       加载一个进程,借助PATH环境变量 

       int execlp(const char *file, const char *arg, ...);              成功:无返回;失败:-1

       参数1:要加载的程序的名字。该函数需要配合PATH环境变量来使用,当PATH中所有目录搜索后没有参数1则出错返回。

      该函数通常用来调用系统程序。如:ls、date、cp、cat等命令。

   execl函数(l是list的缩写)

       加载一个进程,通过路径+程序名来加载。

       int execl(const char *path, const char *arg, ...);             成功:无返回;失败:-1

       对比execlp,如加载"ls"命令带有-l,-F参数

       execlp("ls", "ls", "-l", "-F", NULL);  使用程序名在PATH中搜索。

       execl("/bin/ls", "ls", "-l", "-F", NULL);    使用参数1给出的绝对路径搜索。

  • 回收子进程

       孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

       僵尸进程: 进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。

       特别注意,僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程已经终止。

       1.wait函数

          一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。        

        父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:

              ①阻塞等待子进程退出(父进程进行回收,执行这个函数后,如果子进程没有死,会在这里一直等待子进程结束然后回收资源。)

              ②回收子进程残留资源

              ③获取子进程结束状态(退出原因)。       

        pid_t wait(int *status);          成功:清理掉的子进程ID;失败:-1 (没有子进程)

         当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)。

         可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组(后三个了解,前四个要掌握):

        1.  WIFEXITED(status) 为非0    →进程正常结束

              WEXITSTATUS(status) 如上宏为真,使用此宏→获取进程退出状态 (exit的参数)

       2.    WIFSIGNALED(status) 为非0 →进程异常终止

              WTERMSIG(status) 如上宏为真,使用此宏→取得使进程终止的那个信号的编号。

       3.    WIFSTOPPED(status) 为非0 →进程处于暂停状态

              WSTOPSIG(status) 如上宏为真,使用此宏→取得使进程暂停的那个信号的编号。

              WIFCONTINUED(status) 为真→进程暂停后已经继续运行

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>

int main(void)
{
	pid_t pid, wpid;
	int status;

	pid = fork();

	if(pid == -1){
		perror("fork error");
		exit(1);
	} else if(pid == 0){		//son
		printf("I'm process child, pid = %d\n", getpid());
#if 1
		execl("./abnor", "abnor", NULL);
		perror("execl error");
		exit(1);
#endif
		sleep(1);				
		exit(10);
	} else {
		//wpid = wait(NULL);	//传出参数
		wpid = wait(&status);	//传出参数

		if(WIFEXITED(status)){	//正常退出
			printf("I'm parent, The child process "
					"%d exit normally\n", wpid);
			printf("return value:%d\n", WEXITSTATUS(status));

		} else if (WIFSIGNALED(status)) {	//异常退出
			printf("The child process exit abnormally, "
					"killed by signal %d\n", WTERMSIG(status));
										//获取信号编号
		} else {
			printf("other...\n");
		}
	}

	return 0;
}

   2.waitpid函数

      注意:一次wait调用只能回收一个子进程,如果有五个子进程需要回收的时候,调用一次wait只能回收最早死亡的子进程。     

      pid_t waitpid(pid_t pid, int *status, int options);         成功:返回清理掉的子进程ID;失败:-1(无子进程)

      特殊参数和返回情况:

            参数pid(重点掌握前面的两个):

                  > 0 回收指定ID的子进程 

                  -1 回收任意子进程(相当于wait

                   0 回收和当前调用waitpid一个组的所有子进程

                   < -1 回收指定进程组内的任意子进程

           返回0:参3为WNOHANG(把waitpid设置成非阻塞,当传0值的时候就是阻塞了,和wait一样),且子进程正在运行。

        注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值