【Linux】进程创建(fork函数)及其等待与退出
进程创建
进程是把该程序运行起来加载到内存,受OS控制,建立PCB,OS把PCB管理好就是把进程管理好。
以fork为例:
fork函数
在Linux中fork函数是非常重要的函数,会创建子进程,原进程是父进程。
子进程返回0,父进程返回子进程id。
进程调用fork,当控制转移到内核中的fork代码后,内核做以下动作:
分配新的内存块和内核数据给子进程;
将父进程部分数据结构内容拷贝至子进程;
添加子进程到系统进程列表当中;
fork返回,开始调度器调度;
当一个进程调度用fork之后,就是两个二进制代码相同的进程。而且他们都运行到相同的地方。但每次进程都将可以开始他们自己的进程。
int main(){pid_t pid;printf("Before: pid is %d\n",getpid());if((pid = fork()) == -1)perror("fork()"),exit(1);printf("After:pid is %d,fork return %d\n",getpid(),pid);sleep(1);return 0;}
这里输出3行,一行before,两行After。进程5643先打印before消息,然后又打印after,另一个after消息由5644打印。但5644没有打印after,证明:
fork之后父进程独立执行,fork之后,父子两个执行流分别执行。注意:fork之后,谁先执行完全由调度器决定。
fork之后,父子进程代码共享,数据以写时拷贝的方式各自私有一份。
fork常规用法
父子进程执行不同的代码;
一个进程要执行一个不同的程序。
fork调用失败的原因
系统中有太多的进程
实际用户的进程数超过了限制
进程终止
进程退出场景
代码运行完毕,结果正确
代码运行完毕,结果不正确
代码异常终止
进程退出的方法
从main中返回
调用exit
_exit
异常退出:ctrl+从,信号终止
_exit:终止进程,数据清空;
exit:终止进程,数据不清空;
return退出:从main函数返回
进程等待
进程等待的必要性
子进程退出,父进程不管不顾,就可能造成“僵尸进程”的问题,进而造成内存泄露;
进程一旦变成僵尸状态,那就刀枪不入,kill -9也无能为力;
父进程派给子进程的任务完成如何,我们需要知道;
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
进程等待的方法
wait方法
成功返回被等待进程pid,失败返回-1;
waitpid方法
pid_t waitpid(pid_t pid,int *status,int options);
返回值:正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用出错,返回-1,这时errno会被设置成相应的值以指示错误所在。
pid:
pid = -1,等待任一个子进程,与wait等效;
pid >0,等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status):若为正常终止子进程返回的状态,则为真。
WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码。
options:
WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立刻返回并释放资源,获得子进程信息;
如果在任意时刻调用wait/waitpid,子进程存在并还没有退出时,进程可能阻塞;
若不存在子进程,立即出错返回。
获取子进程的status
status是一个参数,是一个输出型参数,由操作系统填充,wait和waitpid都存在;
若参数为NULL,表示不关心子进程的退出状态,否则,OS根据该参数,将子进程的退出信息返回给父进程;
status可以当做位图来看:只研究低16比特位
正常终止:8-15位是退出状态;
被信号杀死:看低8位,查看终止信号。
测试代码:(正常终止与信号kill)
#include <stdio.h>#include <stdlib.h>#include <sys/wait.h>#include <error.h> int main() { pid_t id = fork(); if(id < 0) { perror("fork"); exit(1); } if(id == 0) { printf("I am child...\n"); sleep(20); exit(10); } else { int st; int ret = wait(&st); if(ret > 0 && (st & 0x7F) == 0) { //正常终止 printf("child exit code:%d\n",(st >> 8)&0x7F); } else if(ret > 0) { //异常退出 printf("sig code:%d\n",st&0x7F); } } }
正常终止,等20秒后
异常终止:
示例:
int status;
主要的原因是,fork() 函数调用一次,返回两次。两次返回的区别是:子进程的返回值是0,父进程返回值为新子进程的进程ID。返回后,父进程执行waitpid(t, &status, 0)等待子进程结束,而子进程进入另一个分支执行system("vi temp.txt");exit(0);,父子间并不冲突,可以形容这段代码父进程与子进程都执行了一次判断