9.进程控制(上)
一、进程终止
代码运行完毕,结果正确。
代码运行完毕,结果不正确。
代码异常终止。(退出码无意义)
子进程也是进程->父进程创建->子进程执行的结果
进程一旦出现异常,一般是进程收到了信号。
return 本质就是将返回值放到寄存器。
即使不写return 0,返回值也是0的原因:无非就是少了将 ret 放到寄存器这一句指令,寄存器默认存的值为0,拿到的程序退出码(语言层面)还是0。
int main(); main函数返回值,通常表明程序的执行情况。
echo $? 打印最近一个程序退出时的退出码->进程退出码->写到进程的task_struct内部的
结果正确,退出码为0。
结果不正确,退出码非0,{1,2,3,...},为了通过错误码得知错误信息。
strerror,<string.h>,根据错误码返回对应的字符串。
[0,133],一共有134种错误码。
errno:整形变量,<errno.h>,记录上一个错误码。
测试代码:
#include <stdio.h> #include <string.h> #include <errno.h> int main() { FILE* pf = fopen("test.txt","r"); if(pf==NULL) { printf("strerror:%s\n",strerror(errno)); return errno; } fclose(pf); return 0; }
进程退出的方式(重点)
main函数结束,表示进程结束。
其他函数结束,只表示自己函数调用完成,返回。
main:(return)
exit:(status)
任何地方调用exit,表示进程结束!并返回给父进程,子进程的退出码。
exit(C)vs _exit(系统):
_exit()和exit()是上下层的关系,exit()封装了系统调用_exit()。
我们之前讨论的缓冲区。应该在哪里?
一定不是操作系统内部的缓冲区,否则_exit退出会导致内存泄漏。
库缓冲区,C语言提供的缓冲区。
二、进程等待(重点)
进程等待的必要性
为什么要等待?
1)防止造成“僵尸进程”的问题,进而导致内存泄漏。
2)父进程派给子进程的任务完成的情况(结果对,结果不对,异常退出),父进程要知道。
总结:父进程通过进程等待的方式,回收子进程资源(重要)和 获取子进程退出信息 。
进程等待的方式
wait,<sys/types.h>,<sys/wait.h>
pid_t wait(int *status); status为输出型参数,输出等待子进程的进程退出码。
等待成功,返回子进程pid,等待失败返回-1。
wait是阻塞调用,父进程会一直阻塞。
waitpid的使用(重点)
pid_t waitpid(pid_t pid, int *status, int options);
pid为指定等待子进程的pid(特别的,pid为-1,等待任意一个子进程,pid<-1,等这个pid绝对值的子进程)
status为输出型参数,输出等待子进程的进程退出码
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)WEXITSTATUS(status): 若 WIFEXITED非零 ,提取子进程退出码。(查看进程的退出码)options为选项,可以选择等待的方式为 阻塞等待 或 非阻塞等待。(默认为0,阻塞等待)
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。(非阻塞等待)waitpid(-1,NULL,0); 等同于 wait(NULL);
其中NULL,表示父进程不关心子进程退出信息。
测试代码如下:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main() { pid_t pid = fork(); if(pid==0) { //子进程 int cnt = 5; while(cnt--) { sleep(1); printf("子进程pid:%d\n",getpid()); } exit(1); } //pid_t ret = wait(NULL); int status; pid_t ret = waitpid(-1,&status,0); printf("等待的子进程pid:%d,status:%d\n",ret,status); return 0; }
POSIX标准定义:
status
的位操作需严格遵循从0开始的编号方式。例如:终止信号编号:存储在第0~7位(即低8位),通过
status & 0x7F
提取。退出码:存储在第8~15位(即高8位),通过
(status >> 8) & 0xFF
提取。
core dump
标志:第7位(从0开始计数,即status & 0x80
的非零值)。异常判断:
1)低7个比特位,0
2)一旦低7个比特位不为0,异常退出,退出码无意义!
main函数返回值要写到PCB里,PCB里有对应的exit_code退出码,exit_signal信号码,父进程通过系统调用waitpid获取子进程的状态码,getpid()和getppid()同理,PCB里有存。
为什么?
因为子进程结束后,进入僵尸状态,等待父进程回收,其中进程地址空间、代码和数据以及页表可以释放,但是子进程的PCB(task_struct)需要保留,
WNOHANG 子进程还没退出就立即返回,非阻塞调用。
返回值:
大于0:等待结束
等于0:调用结束,但子进程没有退出
小于0:等待失败
非阻塞调用:可以让等待者,做自己的事情,充分利用资源。