目录1.僵尸进程与孤儿进程1.1 孤儿进程1.2 僵尸进程Z2.进程状态3.进程退出与进程等待3.1 进程退出3.2 进程等待3.2.1 wait和waitpid对比3.3 WEXITSTATUS 和 WIFEXITED1.僵尸进程与孤儿进程1.1 孤儿进程父进程结束了子进程还没有结束就成为了孤儿进程linux系统所有孤儿进程会被init进程进程号为1自动领养当孤儿进程执行结束时init进程会进行回收孤儿进程依然占据着cpu和内存。孤儿进程示例1.2 僵尸进程Z僵尸进程是指子进程已经结束了但是父进程没有回收子进程。子进程此时不会占据cpu资源和内存但会占据进程号系统的进程号是有限的如果一直占用不回收进程号就不够用了。父进程调用wait/waitpid来回收子进程如果父进程结束了或者被杀死了那僵尸进程会变成孤儿进程被系统回收。僵尸进程示例可以输入这串命令观察进程状态while : ;do ps ajx | head -1 ps ajx | grep zonbine |grep -v grep ;sleep 1 ;done2.进程状态常见的进程状态有运行阻塞暂停。运行状态用R表示代表程序正在运行或者在队列中等待被调度阻塞状态分为D可中断睡眠和S不可中断睡眠S通常为等待输入信号可以是键盘或者调用了sleep函数。按下ctrl c或者kill进程就会立刻响应D不能被任何信号打断为了数据保护大文件拷贝数据库拷入磁盘等场景。暂停态分为T和tT为ctrl z主动挂起 t是由调试器gdb触发每一个进程都有一个PCB进程控制块PCB是进程存在的唯一标志。具体为一个task_struct的结构体。操作系统为了管理进程先描述在组织。组织形式就是用红黑树哈希链表。一个CPU有一个调度队列代码和数据是和进程PCB分开的当进程执行时代码数据会从磁盘加载到内存。抽象如下设备也有一个设备队列结构和调度队列类似当进程处于阻塞状态时就会将进程的PCB和等待响应的设备以指针的形式连接对进程的操作本质上就是对链表的增删改查。抽象如下3.进程退出与进程等待3.1 进程退出进程结束都会有退出码运行完结果正确退出码为0运行完结果错误退出码非0异常退出退出码无意义每个进程的PCB都会有退出码的信息并把退出码返回给父进程父进程通过wait来获得子进程的退出信息。一共有140个退出信息可以通过steerror函数查看echo $?会打印最近一个进程退出时的退出码0: Success 1: Operation not permitted 2: No such file or directory 3: No such process 9: Bad file descriptor 13: Permission denied 110: Connection timed out ...3.2 进程等待父进程可以通过fork创建子进程每个进程都有自己的pid可以通过getpid和getppid查看。fork返回值为0代表子进程返回值大于0子进程pid代表父进程。父子进程是按照时间片交替执行的。如果想让子进程执行完再去执行父进程叫做进程等待是通过wait系统调用实现的此时父进程就变成阻塞状态pid_t waitpid(pid_t pid,int* stream,int options)第二个参数会存储子进程的退出码低7个比特位全0如果非0代表退出码无意义如果不想让父进程一直处于阻塞状态第三个参数可以设为WNOHANG通常搭配循环来完成。返回值大于0等待结束等于0调用结束但子进程没有退出小于0等待失败。父进程需要在一个循环里反复调用来检查状态#include stdio.h #include unistd.h #include sys/wait.h #include stdlib.h int main() { pid_t pid fork(); if (pid 0) { printf(子进程(PID: %d)启动运行3秒...\n, getpid()); sleep(3); printf(子进程工作完成准备退出。\n); exit(42); } else { int status; printf(父进程开始非阻塞轮询等待子进程...\n); while (1) { pid_t ret waitpid(pid, status, WNOHANG); if (ret 0) { printf(父进程成功回收子进程(PID: %d)退出码为: %d\n, ret, WEXITSTATUS(status)); break; } else if (ret 0) { printf(子进程还没结束父进程先去处理点其他任务...\n); sleep(1); } else{ perror(waitpid error); break; } } printf(父进程所有任务处理完毕退出。\n); } return 0; }3.2.1 wait和waitpid对比wait是c语言封装的函数底层调用的就是waitpid。wait只需要传一个参数其余参数都有默认值函数原型为pid_t wait(int *wstatus);传入NULL表示父进程不关心子进程怎么退出的只是单纯的阻塞等待把子资源回收传status搭配WEXITSTATUS 和 WIFEXITED获取退出信息。wait只能等待任意一个子进程不能像waitpid那样通过第一个参数pid指定等待的子进程。3.3 WEXITSTATUS 和 WIFEXITED这是两个宏通常在一起组合使用来判断进程的退出情况。如果WIFEXITED为真代表子进程正常退出WEXITSTATUS打印退出进程的退出码if (WIFEXITED(status)) { printf(正常退出退出码: %d\n, WEXITSTATUS(status)); }status是waitpid函数的第二个参数使用WEXITSTATUS查看退出码就不需要右移8位了