进程退出函数
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;
}