进程终止
进程正常退出通过调用exit函数,异常退出通常是ctr+c信号终止。
exit函数其实最终要调用_exit函数完成进程终止,其步骤是:
- 执行atexit 或 on_exit定义的清理函数
- 关闭所有打开的流,所有缓存数据均被写入
- 调用_exit
void _exit(int status); status定义了进程终止状态,需要注意status的范围是0~255,即低八位可以被父进程获取到。
exit和return的区别
使用vfork后,子进程调用exit() 直接退出子进程没有修改函数栈,所以,父进程得以顺利执行。
return会释放局部变量,并弹栈,回到上级函数执行。exit直接退掉进程,没有善后工作。
atexit函数的作用是在函数退出时执行它的参数函数。
void funA()
{
printf("funA\n");
}
int main()
{
atexit(funA);
printf("hello");
_exit(0);
}
没有任何输出,说明进程直接被终止。
void funA()
{
printf("hello funA\n");
}
int main(void)
{
atexit(funA);
printf("main\n");
exit(0);
}
main
hello funA
执行结果说明exit函数调用_exit前执行了atexit函数,并且刷新了缓冲区内容。
atexit的原理其实就是在调用时注册回调函数,入栈,在调用exit时出栈并执行回调函数。
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
void (*pfun)(void);
struct Node *next;
}node;
void at_exit(void (*f)(void), node *pHead){
node *pnode = malloc(sizeof(node));
pnode->pfun = f;
pnode->next = NULL;
//头插
pHead->next = pnode;
}
void Exit(node *pHead){
//出栈
while(pHead->next){
pHead->next->pfun();
pHead->next = pHead->next->next;
}
}
void funA()
{
printf("hello funA\n");
}
int main(void)
{
node Head;
Head.next = NULL;
node *pHead = &Head;
at_exit(funA, pHead);
printf("main\n");
Exit(pHead);
}
补充一个函数属性
void test() __attribute__((constructor));
在声明函数时,可以为函数声明一个属性,例如constructor,在main函数执行前调用。
进程等待
为什么需要进程等待?
- 如果子进程退出,父进程不管不顾,子进程变为僵尸进程,造成内存泄露
- 父进程需要得到子进程的返回状态
pid_t wait(int *status);
等待成功返回等待进程pid,失败返回-1. status是子进程退出状态,不关心时设为NULL。
waitpid 是wait的升级版
pid_t waitpid(pid_t pid, int *status, int options);
pid参数:
- pid > 0 等待进程ID和pid相等的子进程
- pid = -1 等待任意子进程,和wait等效
- pid < -1 等待进程组ID和pid相等的组内任一子进程
options:
WNOHANG ,非阻塞式等待,若子进程没有结束,则不等待,返回0.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid = fork();
if(pid == 0){
//子子
printf("子进程pid: %d\n", getpid());
pid_t pidChild = fork();
if(pidChild == 0){
printf("子子进程pid: %d\n", getpid());
while(1){
printf("X");
fflush(stdout);
sleep(1);
}
}
exit(-1);
}
else{
int sta;
pid_t p = wait(&sta);
printf("父进程回收的pid = %d, 退出状态 = %d\n", p, (sta>>8)&0xFF);
while(1){
printf("O");
fflush(stdout);
sleep(1);
}
}
}
子进程pid: 15307
父进程回收的pid = 15307, 退出状态 = 255
O子子进程pid: 15308
OXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXOXXOXOXOXOXOXOXO^C
输出结果说明:
父进程等待回收了子进程,但是退出状态-1 变为255,说明status只使用int低八位且是unsigned.
然后父进程和子子进程交叉执行。