1. 进程退出
主函数退出
exit函数(exit, _exit, _Exit)
被动结束
exit是标准C库函数,会刷新缓冲区,_exit和_Exit函数是系统调用,退出进程不会刷新缓冲区
2. 进程资源回收
僵尸进程:僵尸进程:进程已结束,但其资源空间未被其父进程回收
如何避免僵尸进程?
子进程结束,其父进程使用wait/waitpid回收资源空间
让子进程成为一个孤儿进程,孤儿进程会被系统中的进程收养,由系统进程负责回收
孤儿进程:父进程先消亡,由该父进程产生的子进程成为孤儿进程,将会被系统进程所收养(守护进程)
2.1 exit 库函数
退出状态,终止的进程会通知父进程,自己使如何终止的。如果是正常结束(终止),则由exit传入的参数。如果是异常终止,则有内核通知异常终止原因的状态。任何情况下,父进程都能使用wait,waitpid获得这个状态,以及资源的回收。
void exit(int status)
exit(1);
功能:
让进程退出,并刷新缓存区
参数:
status:进程退出的状态
返回值:
EXIT_SUCCESS 0
EXIT_FAILURE 1
return:当该关键字出现在main函数中时候可以结束进程
如果在其他函数中则表示结束该函数。
2.2 _exit 系统调用
void _exit(int status);
功能:
让进程退出,不刷新缓存区
参数:
status:进程退出状态
返回值:
2.3 wait函数
pid_t wait(int *status);
功能:该函数可以阻塞等待任意子进程退出
并回收该进程的状态。
一般用于父进程回收子进程状态。
参数:status 进程退出时候的状态
如果不关心其退出状态一般用NULL表示
如果要回收进程退出状态,则用WEXITSTATUS回收。
返回值:成功 回收的子进程pid
失败 -1;
1)如果所有的子进程都在运行,在阻塞
2)如果一个子进程终止,正在等待的父进程则获得终止状态,获得子进程的状态后,立刻返回
3)如果没有子进程,则立即出错退出
参数wstatus的相关宏
WIFEXITED(status) 是不是正常结束
WEXITSTATUS(status) 使用这个宏去拿exit结束状态
WIFSIGNALED(status) 是不是收到了信号而终止的
WTERMSIG(status)如果是信号终止的,那么是几号信号
2.4 waitpid函数
pid_t waitpid(pid_t pid, int *status, int options);
功能:回收指定进程的资源(和wait功能相似,比wait更灵活)
参数:
pid:
1) <-1 回收指定进程组内的任意子进程 (-100.等待GID=100的进程组中的任意子进程)
2) -1 回收任意子进程,组内外
3) 0 回收和当前调用waitpid一个组的所有子进程,组内
4) > 0 回收指定ID的子进程
status:子进程退出时候的状态,
如果不关注退出状态用NULL;
options 选项:
0 表示回收过程会阻塞等待
WNOHANG 表示非阻塞模式回收资源。
返回值:
成功:返回接收资源的子进程pid
失败 -1
设定为非阻塞且没有回收到子进程返回0
非阻塞方式+轮询方式:
waitpid:父进程有自己的任务执行。
waitpid(-1, NULL, WNOHANG);
2.5 exec函数族
在一个进程中执行外部的一段程序
原因:exec会将外部程序的指令替换到进程的文本,因此主程序exec函数之后的内容将不会被执行
int execl(const char *path, const char *arg, ...
/* (char *) NULL */);
path:执行的外部程序对应的路径和名称
arg:执行外部程序需要的参数
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
file:要执行的外部程序的名称
arg:执行外部程序需要的参数
int execv(const char *path, char *const argv[]);
path:执行的外部程序对应的路径和名称
argv:执行外部程序需要的参数存放到数组中
int execvp(const char *file, char *const argv[]);
file:要执行的外部程序的名称
argv:执行外部程序需要的参数存放到数组中
这些函数的区别
1)前4个使用路径名作为参数,后面两个使用文件名做参数
当filename中,含有/时视为路径名,否则就按PATH变量,在指定目录下查找可执行文件。
2)相关的参数表传递
l表示list,v表示vector
execl,execlp,execle,需要将参数一个一个列出,并以NULL结尾。
execv,execvp,execve,需要构造一个参数指针数组,然后将数组的地址传入。
3)以e结尾的函数,可以传入一个指向环境字符串的指针数组的指针。
其他未指定环境变量,使用父进程继承过来的。
execve 是真正的系统调用
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错
则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。
2.6 strtok函数
char *strtok(char *str, const char *delim)
功能:分解字符串
参数:
str -- 要被分解成一组小字符串的字符串。
delim -- 包含分隔符的 C 字符串。
返回值:
该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针
分割原理:遇到需要切割的位置将原有内容修改为‘\0’,因此在遇到存储在字符串常量区的字符串时,使用strtok函数会发生段错误
解决方法:将存在在字符串常量区的字符串strcpy在某个字符数组中,再对该数组中的字符串内容进行操作
2.7system函数
#include <stdlib.h>
int system(const char *command);
参数:
command
是一个指向以空字符结尾的字符串的指针,该字符串包含要在操作系统的命令行中执行的命令。
返回值:
- 如果
command
为NULL
且系统支持命令处理程序,则返回非零值;若不支持,则返回 0。 - 如果
command
不为NULL
,返回值取决于操作系统的实现。通常,返回值是命令执行后的退出状态码,若执行失败则返回 -1。