【Linux系统编程】进程退出和回收进程资源

00. 目录

01. 进程退出函数

#include <stdlib.h>

void exit(int status);
功能:
	结束调用此函数的进程。
参数:
	status:返回给父进程的参数(低 8 位有效),至于这个参数是多少根据需要来填写。
返回值:
	无



#include <unistd.h>

void _exit(int status);
功能:
	结束调用此函数的进程。
参数:
	status:返回给父进程的参数(低 8 位有效),至于这个参数是多少根据需要来填写。
返回值:
	无

    
#include <stdlib.h>

void _Exit(int status);
功能:
	结束调用此函数的进程。
参数:
	status:返回给父进程的参数(低 8 位有效),至于这个参数是多少根据需要来填写。
返回值:
	无

exit() 和 _exit() 函数功能和用法是一样的,无非时所包含的头文件不一样,还有的区别就是:exit()属于标准库函数,_exit()属于系统调用函数。

在这里插入图片描述

02. 进程退出讨论

下面的例子验证调用 exit() 函数,会刷新 I/O 缓冲区 。

测试程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
	printf("hello world"); // 打印,没有换行符"\n"
	
	exit(0);      // 结束进程,标准库函数,刷新缓冲区,printf()的内容能打印出来
	// _exit(0);  // 结束进程,系统调用函数,printf()的内容不会显示到屏幕
	
	while(1);	// 不让程序结束
 
	return 0;
}

测试结果如下:

在这里插入图片描述

上面的例子,结束进程的时候改为调用 _exit(),代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
	printf("hello world"); // 打印,没有换行符"\n"
	
	//exit(0);      // 结束进程,标准库函数,刷新缓冲区,printf()的内容能打印出来
	_exit(0);  // 结束进程,系统调用函数,printf()的内容不会显示到屏幕
	
	while(1);	// 不让程序结束
 
	return 0;
}

printf() 的内容是不会显示出来的,运行结果如下:

在这里插入图片描述

接下来,我们一起验证一下结束函数( return )和结束进程( exit() )的区别。

测试代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
void fun()
{
	sleep(2);
	
	return;  // 结束 fun() 函数
	
	while(1);
}
 
 
int main(int argc, char *argv[])
{
	fun();
	
	printf("after fun\n");
	
	while(1);	// 不让程序结束
 
	return 0;
}

测试结果如下:

在这里插入图片描述

通过上面的运行结果得知,return 的作用只是结束调用 return 的所在函数,只要这个函数不是主函数( main() ),只要主函数没有结束,return 并不能结束进程

我们将上面例子 fun() 里的 return 改为 exit():

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
void fun()
{
	sleep(2);
	
	exit(0);  // 结束当前进程
	
	while(1);
}
 
int main(int argc, char *argv[])
{
	fun();
	
	printf("after fun\n");
	
	while(1);	// 不让程序结束
 
	return 0;
}

测试结果:

在这里插入图片描述

03. 回收进程资源

3.1 概述

在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块PCB的信息(包括进程号、退出状态、运行时间等)。

当一个进程正常或异常终止时,内核就向其父进程发送 SIGCHLD 信号,相当于告诉父亲他哪个儿子挂了,而父进程可以通过 wait() 或 waitpid() 函数等待子进程结束,获取子进程结束时的状态,同时回收他们的资源(相当于,父亲听听死去儿子的遗言同时好好安葬它)。

父进程可以通过调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

wait() 和 waitpid() 函数的功能一样,区别在于,wait() 函数会阻塞,waitpid() 可以设置不阻塞,waitpid() 还可以指定等待哪个子进程结束。

注意:

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

3.2 wait函数

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
功能:
	等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。
参数:
	status : 进程退出时的状态信息。
返回值:
	成功:已经结束子进程的进程号
	失败: -1
        
这个退出信息在一个 int 中包含了多个字段,直接使用这个值是没有意义的,我们需要用宏定义取出其中的每个字段。



下面我们来学习一下其中最常用的两个宏定义,取出子进程的退出信息:

WIFEXITED(status)
	如果子进程是正常终止的,取出的字段值非零。
WEXITSTATUS(status)
	返回子进程的退出状态,退出状态保存在 status 变量的 8~16 位。在用此宏前应先用宏 WIFEXITED 判断子进程是否正常退出,正常退出才可以使用此宏。


从本质上讲,系统调用 waitpid() 和 wait() 的作用是完全相同的,但 waitpid() 多出了两个可由用户控制的参数 pid 和 options,从而为我们编程提供了另一种更灵活的方式。

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);
功能:
	等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。

参数:
	pid : 参数 pid 的值有以下几种类型:
	  pid > 0  等待进程 ID 等于 pid 的子进程。
	  pid = 0  等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。
	  pid = -1 等待任一子进程,此时 waitpid 和 wait 作用一样。
	  pid < -1 等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。

	status : 进程退出时的状态信息。和 wait() 用法一样。

	options : options 提供了一些额外的选项来控制 waitpid()0:同 wait(),阻塞父进程,等待子进程退出。
			WNOHANG:没有任何已经结束的子进程,则立即返回。
			WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态。(由于涉及到一些跟踪调试方面的知识,加之极少用到)
				 
返回值:
	waitpid() 的返回值比 wait() 稍微复杂一些,一共有 3 种情况:
		1) 当正常返回的时候,waitpid() 返回收集到的已经回收子进程的进程号;
		2) 如果设置了选项 WNOHANG,而调用中 waitpid() 发现没有已退出的子进程可等待,则返回 03) 如果调用中出错,则返回-1,这时 errno 会被设置成相应的值以指示错误所在,如:当 pid 所对应的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid() 就会出错返回,这时 errno 被设置为 ECHILD;

waitpid测试代码

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

int main(void)
{
    pid_t pid = -1;
    int i = 0;
    int status;

    //创建一个子进程
    pid = fork();

    if (pid < 0)
    {
        perror("fork"); 
    }

    //子进程
    if (0 == pid)
    {
        for (i = 0; i < 10; i++)
        {
            printf("I am child process pid: %d %d\n", getpid(), i); 
            //usleep(500000);
            sleep(1);
        }

        exit(10);
    }



    //父进程
    //wait(&status);  //等待子进程的状态改变
    //WNOHANG 如果没有子进程退出 立即返回
    //waitpid(pid, &status, WNOHANG);
    waitpid(pid, &status, WUNTRACED);
    
    //如果正常退出 就返回真
    if (WIFEXITED(status))
    {
        printf("正常退出 %d...\n",  WEXITSTATUS(status));
    }
    else if (WIFSIGNALED(status))
    {
        printf("被信号杀死  %d..\n", WTERMSIG(status)); 
    }
    else if (WIFSTOPPED(status))
    {
        printf("the child process was stopped by delivery of a signal %d\n", WSTOPSIG(status)); 
    }
    else if (WIFCONTINUED(status))
    {
        printf("the child process was resumed by delivery of SIGCONT\n"); 
    }



    printf("I am parent process\n"); 
    printf("child pid: %d pid: %d ppid: %d\n", pid, getpid(), getppid());

    return 0;
}

执行结果:

在这里插入图片描述

04. 附录

4.1 参考博客:【Linux系统编程】进程的控制:结束进程、等待进程结束

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值