Linux高级编程 8.20 进程回收和exec函数族

一、进程空间的回收

wait/waitpid

1.wait

pid_t wait(int *status);

功能:该函数可以阻塞等待任意子进程退出
      并回收该进程的状态。
      一般用于父进程回收子进程状态。

参数:status 进程退出时候的状态
      如果不关心其退出状态一般用NULL表示
      如果要回收进程退出状态,则用WEXITSTATUS回收。

返回值:成功 回收的子进程pid
             失败 -1;

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

int main(int argc, const char *argv[])
{
	pid_t ret=fork();
	if(ret>0)
	{
		printf("father's pid:%d ppid:%d\n",getpid(),getppid());
		wait(NULL);//不会产生僵尸进程,因为一旦子消亡,父就立马回收了
		printf("after wait\n");
		sleep(5);
	}
	else if(ret==0)
	{
		printf("child's pid:%d ppid:%d\n",getpid(),getppid());
		sleep(3);
		printf("child terminal\n");
		exit(1);
	}
	else
	{
		perror("fork");
		return 1;
	}
	
	return 0;
}

WIFEXITED(status)  是不是正常结束(宏返回真代表子进程正常结束)

WEXITSTATUS(status) 从传入的 status 中提取出进程的退出状态。这个宏通常与 wait() 或 waitpid() 函数一起使用,用于获取子进程的终止状态。
当一个进程(通常是子进程)结束时,它会返回一个状态码给其父进程。这个状态码被封装在一个整数值中,父进程可以通过 wait() 或 waitpid() 函数获取这个状态。状态值可能包含多种信息,包括进程的退出状态、是否被信号终止等。

WIFSIGNALED(status) 判断是不是收到了信号而终止的
              

WTERMSIG(status)如果是信号终止的,那么是几号信号。

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

int main(int argc, const char *argv[])
{
	pid_t ret=fork();
	if(ret>0)
	{
		printf("father's pid:%d ppid:%d\n",getpid(),getppid());

		int status;
		while(1)
		{
			pid_t pid=waitpid(ret,&status,WNOHANG);
			if(ret==pid)
			{
				if(WIFEXITED(status))//判断子进程是否正常结束
				{
					//正常结束的子进程才能获得返回值
					printf("child quit values:%d\n",WEXITSTATUS(status));
				}
				if( WIFSIGNALED(status))//异常结束
				{
					printf("child abnormal,signal num:%d\n",WTERMSIG(status));
				}
				break;
			}
			else if(0==pid)
			{
				printf("child is alive,wait latter\n");
			}
		}
		printf("after wait,%d\n",status);
	}
	else if(ret==0)
	{
		printf("child's pid:%d ppid:%d\n",getpid(),getppid());
		sleep(10);
		printf("child terminal\n");
		exit(50);
	}
	else
	{
		perror("fork");
		return 1;
	}

	return 0;
}

pid_t wait(int *status);

    1)如果所有的子进程都在运行,在阻塞
    2)如果一个子进程终止,正在等待的父进程则获得终止状态,获得子进程的状态后,立刻返回。
    3)如果没有子进程,则立即出错退出。

2.waitpid    

waitpid(-1,status,0)=wait(status);(两个种写法等效)

pid_t waitpid(pid_t pid, int *status, int options);

参数:

1)pid:要等待的子进程的进程 ID(PID)。

  • pid > 0:等待指定的子进程(PID 为 pid 的子进程)。
  • pid == 0:等待与当前进程属于同一个进程组的任意子进程。
  • pid < -1:等待进程组 ID 为 -pid 的所有子进程。
  • pid == -1:等待所有子进程(类似于 wait 函数的行为)

     waitpid (-1,a,0)  == wait(a);

2)status 子进程退出时候的状态,
              如果不关注退出状态用NULL;
3)options 选项:
                  0  表示回收过程会阻塞等待
                WNOHANG 表示非阻塞模式回收资源。
返回值:成功 返回接收资源的子进程pid
             失败  -1
             0,EAGAIN

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

int main(int argc, const char *argv[])
{
	pid_t ret=fork();
	if(ret>0)
	{
		printf("father's pid:%d ppid:%d\n",getpid(),getppid());

		int status;
		while(1)
		{
			pid_t pid=waitpid(ret,&status,WNOHANG);
			if(ret==pid)
			{
				if(WIFEXITED(status))//判断子进程是否正常结束
				{
					//正常结束的子进程才能获得返回值
					printf("child quit values:%d\n",WEXITSTATUS(status));
				}
				if( WIFSIGNALED(status))//异常结束
				{
					printf("child abnormal,signal num:%d\n",WTERMSIG(status));
				}
				break;
			}
			else if(0==pid)
			{
				printf("child is alive,wait latter\n");
			}
		}
		printf("after wait,%d\n",status);
	}
	else if(ret==0)
	{
		printf("child's pid:%d ppid:%d\n",getpid(),getppid());
		sleep(10);
		printf("child terminal\n");
		exit(50);
	}
	else
	{
		perror("fork");
		return 1;
	}

	return 0;
}

练习:
    设计一个多进程程序,用waitpid函数指定回收
    其中的某个进程资源并将其状态打印输出。
    其他的进程都以非阻塞方式进行资源回收。

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

int main(int argc, const char *argv[])
{
	pid_t ret=fork();
	if(ret>0)
	{
		printf("father's pid:%d ppid:%d\n",getpid(),getppid());

		int status;
		while(1)
		{
			pid_t pid=waitpid(ret,&status,WNOHANG);
			if(ret==pid)
			{
				if(WIFEXITED(status))//判断子进程是否正常结束
				{
					//正常结束的子进程才能获得返回值
					printf("child quit values:%d\n",WEXITSTATUS(status));
				}
				if( WIFSIGNALED(status))//异常结束
				{
					printf("child abnormal,signal num:%d\n",WTERMSIG(status));
				}
				break;
			}
			else if(0==pid)
			{
				printf("child is alive,wait latter\n");
			}
		}
		printf("after wait,%d\n",status);
	}
	else if(ret==0)
	{
		printf("child's pid:%d ppid:%d\n",getpid(),getppid());
		sleep(10);
		printf("child terminal\n");
		exit(50);
	}
	else
	{
		perror("fork");
		return 1;
	}

	return 0;
}

二、exec函数族        

execute 执行

exec函数是调用一个外部的可执行程序(只能是可执行文件)

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

1.execl

 execl  函数的原型通常为:

 int execl(const char *path, const char *arg0,..., NULL); 

path  是要执行的程序的路径。 arg0  及后续的参数是传递给新程序的命令行参数,以空指针结尾。

execl  函数只有在执行失败时才会返回 -1 ,成功时不会返回。

eg:使用execl打开火狐的百度网页

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	execl("/usr/bin/firefox","firefox","www.baidu.com",NULL);
	printf("看见就错了\n");
	exit(1);

	return 0;
}

2.execlp

execlp  函数的原型通常为:

int execlp(const char *file, const char *arg, ...);

与  execl  函数的主要区别在于, execlp  会在环境变量  PATH  所指定的目录中搜索要执行的文件。

例如,如果环境变量  PATH  中包含了可执行文件所在的目录,要执行  ls  命令,可这样使用 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	//cat execl.c
	//execlp("cat","cat","execl.c",NULL);

	// env(环境变量)或$echo $PATH(只看PATH内容)  
	// ls -l --color=auto相当于ll
    execlp("ls","ls","-l","--color=auto",NULL);
	
	printf("看见就错了\n");
	exit(1);
	
	return 0;
}

3.execv

execv  函数的原型通常为:

int execv(const char *path, char *const argv[]);

 path  是要执行的程序的路径, argv  是一个字符指针数组,其中  argv[0]  通常是程序名,后续的元素是传递给新程序的命令行参数,数组的最后一个元素是  NULL

eg:利用execv 执行cat 命令  

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	char * const args[]={"cat","execl.c",NULL};
	execv("/bin/cat",args);
	
	//char * const args[]={"./test","123","hello",NULL};
	//execv("/home/linux/ld/820/test",args);	
	printf("看见就错了\n");
	exit(1);

	return 0;
}

4.execvp

execvp  函数的原型通常为:

int execvp(const char *file, char *const argv[]);

与  execv  函数的主要区别在于, execvp  会在环境变量  PATH  所指定的目录中搜索要执行的文件,而  execv  则需要指定完整的文件路径。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    char *const args[]={"ls","-l","--color=auto",NULL};
	execvp(args[0],args);//vector+path
    
	//char *const args[]={"./test","343","balabala",NULL};
	//execvp("/home/linux/ld/820/test",args);
	printf("看见就错了\n");
    exit(1);
    return 0;
}

5.区别

1)execl和execv会搜索当前目录;execlp和execvp,只写文件名的话,只会去PATH下找,不找当前目录。如果文件不在PATH下,就写路径+文件名

2)相关的参数表传递

        l表示list,v表示vector

        execl,execlp需要将参数一个一个列出,并以NULL结尾。

        execv,execvp需要构造一个参数指针数组,然后将数组的地址传入。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值