Linux高级编程5 - 进程的退出与调用函数

进程退出函数

1.exit     库函数                 void exit(int status) 

        status:退出时返回的状态码,通常为0表示成功,非0表示错误,status存放8字节的整数、最大255,大于255的数会被截断。
        exit是 C 标准库中的一个函数,用于正常终止一个程序。调用 exit函数时 退出状态,终止的进程会通知父进程,自己使如何终止的。如果是正常结束(终止),则由exit传入的参数。如果是异常终止,则有内核通知异常终止原因的状态。任何情况下,负进程都能使用wait,waitpid获得这个状态,以及资源的回收。以下操作会依次进行:

        1. 清理已打开的流: 所有用 fopen打开的文件流都会被关闭,缓冲区中的数据也会被刷新到相应的文件中。

        2. 调用atexit 注册的函数: 如果通过atexit函数注册了退出时要执行的回调函数,这些函数会被按照注册顺序倒序调用。

        3. 返回退出状态码: 最后exit会将退出状态码返回给操作系统。

 2. _exit (系统调用)        void _exit(int status);

        status:与exit相同,也是返回给操作系统的退出状态码。

        _exit是一个系统调用,它会立即终止调用进程,不做任何清理工作。与exit不同的是,_exit直接从内核层面终止进程,跳过了 C 运行库的清理过程。因此,调用_exit不会关闭文件流或调用atexit注册的回调函数。

3. atexit (库函数)        int atexit(void (*function)(void));

        function:这是一个函数指针,指向要在进程退出时执行的函数。这个函数不接受任何参数,也不返回任何值。

        atexit是 C 标准库中的一个函数,用于注册进程退出时要调用的函数。通过atexit注册的函数会在exit被调用时按照注册顺序的倒序执行。 

1、进程空间回收

1.wait函数         pid_t wait(int *status);

功能:

        wait 函数用于阻塞当前进程,直到任意一个子进程退出。当子进程退出时,wait 会回收子进程的状态,以防止子进程变成僵尸进程。通常,父进程使用 wait 函数来确保它可以正确处理子进程的终止状态。

参数:

        status:这是一个指向 int 类型的指针,用于存储子进程退出时的状态。如果你不关心子进程的退出状态,可以传入 NULL。如果需要回收子进程的退出状态,可以传入一个有效的指针。

        当 status 不是 NULL 时,可以使用宏 WEXITSTATUS 提取子进程的退出状态。WEXITSTATUS 从 status 中提取子进程的返回码,它通常是在子进程中通过 exit() 或者 return 返回的值。

下面是常就是用的这个宏函数:

        WIFEXITED(status)  是不是正常结束

        WEXITSTATUS(status) 使用这个宏去那返回值,与上面的WIFEXITED(status)  成对使用

        WIFSIGNALED(status) 是不是收到了信号而终止的与成对使用

        WTERMSIG(status)如果是信号终止的,那么是几号信号。与上面的WIFSIGNALED(status)  成对使用

返回值:

        成功:返回已终止的子进程的 PID(进程 ID)。

        失败:返回 -1,并且 errno 被设置为相应的错误代码。失败的常见原因包括调用进程没有子进程,或者调用被信号中断。

2.waitpid函数        pid_t waitpid(pid_t pid, int *status, int options);

pid:

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

        -1:回收任意子进程(等同于 wait)。

        0:回收与调用 waitpid 的进程处于同一组的所有子进程。

        > 0:回收指定 PID 的子进程。

status:与 wait 函数相同,用于存储子进程的终止状态。可以传 NULL,如果不关心状态。

options:

        0:表示以阻塞方式等待子进程。

        WNOHANG:表示以非阻塞方式调用,如果没有子进程退出则立即返回 0。

返回值:

        成功:返回回收资源的子进程 PID。

        失败:返回 -1,并设置 errno。

        0:如果使用了 WNOHANG 且没有子进程退出,则返回 0。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	pid_t ret = fork();
	if(ret > 0)
	{
		printf("father is %d pid %d\n",getpid(),getppid());
		int status;
		pid_t pid = wait(&status);
		if(WIFEXITED(status))//代表子进程正常结束
		{//正常结束的子进程,才能获得退出值
			printf("child quit values %d\n",WEXITSTATUS(status));
		}
		if(WIFSIGNALED(status))//异常结束
		{//异常结束的子进程,获取结束信号
			printf("child unnormal signal num %d\n",WTERMSIG(status));
		}
		printf("after wait\n");
		sleep(5);//
	}
	else if( ret == 0)
	{
		printf("child pid:%d,ppid:%d\n",getpid(),getppid());
		sleep(5);
		printf("child termial\n");
		exit(50);
	}
	else
		;
	return 0;
}

         exit()、_exit()等这些函数执行,只会将结束自生的进程内存空间(3G)、但是该进程的PCB块还是存在内存中的,要至少父类程序调用wait(),waitpid()才能清除子类的PCB块。不清除PCB的话会生成僵尸进程,并且还会占用存储空间。

 kill -2 1924:       

         kill 是用来向某个进程发送信号的命令。

        2表示发送的信号类型,具体来说是 SIGINT信号(信号编号 2)。SIGINT信号通常用于请求中断进程,相当于在终端中按下 Ctrl+C,目的是请求进程停止运行。

        1924是进程的 PID(Process ID),即你想要发送信号的进程的 ID。

整体解释:

        kill -2 1924:向进程 ID 为 1924 的进程发送 SIGINT信号,通常要求它进行干净的退出。如果这个进程正在运行且响应SIGINT信号,它应该会尝试终止自己。

二、启动其它程序

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

        execl、execlp、execv 和execvp是一组用于在 Unix 系统中执行新程序的函数。它们都属于 exec系列函数,并且在成功执行后不会返回,因为当前进程的代码段会被新程序替换。它们之间的主要区别在于参数传递的方式和可执行文件的查找方式。以下是它们的详细解释:

 1.execl                

        语法:int execl(const char *path, const char *arg, ..., NULL);

        描述:execl 使用 path参数指定的路径来执行程序,并通过可变参数列表来传递命令行参数。每个参数都是单独传递的,最后一个参数必须是 NULL,表示参数列表结束。

2. execlp

        语法:int execlp(const char *file, const char *arg, ..., NULL);

        描述:execlp 与 execl类似,但不同之处在于它使用 file 参数指定可执行文件的名称,而不是完整路径。如果file不包含斜杠(/),execlp会在 PATH环境变量指定的目录中搜索可执行文件。

3. execv

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

        描述:execv与execl的区别在于,它通过数组传递命令行参数,而不是可变参数列表。argv 数组中的每个元素都是一个参数,最后一个元素必须是NULL。

4. execvp

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

        描述:execvp 是execv和execlp的结合,使用数组传递参数,并在PATH环境变量指定的目录中搜索可执行文件。

 总结

        execl和execlp:参数逐个传递,execlp自动在PATH中搜索可执行文件。

        execv和 execvp:参数通过数组传递,execvp自动在PATH中搜索可执行文件。

        execl和execv:是可以从当前路径寻找文件,也可以输入地址+文件名进行寻找可执行文件。

        execvp和exclp:只写文件名的话只从PATH下寻找,不会从当前路径下寻找。但是也可以提供路径+文件名寻找可执行文件。

最保险的是写路径+文件名。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    //  firefox www.baidu.com //whereis fireox查看系统可执行文件的地址
    //execl("/usr/bin/firefox","firefox","www.baidu.com",NULL);
    // env echo $PATH  ls -l --color=auto ll
    //execlp("ls","ls","-l","--color=auto",NULL);
    //PAHT中含有这个,可以提供env查看,或者env path
    char *const args[]={"ls","-l","--color=auto",NULL};
    //execv("/bin/ls",args);//vector
    // path
    execvp(args[0],args);//vector+path
    printf("看见就错了\n");
    exit(1);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值