pid_t id= fork(void); id<0:代表创建失败; id==0:代表是子进程; id>0;代表是父进程,并且id是子进程的ID;
调用fork函数后,OS内核做了什么工作?
1.在fork返回之前,创建了子进程的 PCB数据结构,以及拷贝了一份父进程的地址空间和页表;此时页表是出于只读,一旦修改就会写实拷贝;
2.在修改之前,虚拟地址都是一样的,且映射的物理地址也是一样的;
3.将子进程的PCB加入到调度队列中,从此子进程开始自己的旅程;
1.创建子进程让其帮忙执行任务,例如:父进程等待客户端请求,生成子进程来处理请求。 2.进程替换:让子进程去执行其他的程序
1.系统中有太多的进程(系统进程)
2.用户所拥有的进程数量超出限制(用户进程)
echo $?查看上一个进程的退出码
1.在main函数中return 2.调用exit 3.调用_exit
main函数虽然是主函数,但毕竟也是个函数,最后的返回值即使告诉操作系统是否正常结束;
1.退出码有什么作用?
退出码的作用就是告诉父进程,子进程的退出状态-是正常结束了还是出现错误终止了;
2.为什么要用退出码判断进程是否出错,直接printf不好吗?
printf虽然可以检查错误,但是没有人规定检查错误必须用printf;
错误码适合计算机看,而字符串错误信息是给人看的;所以我们通常将其转化为字符串;
3.父进程为啥要关心子进程的状态?
父进程创建子进程的目的就是为了让子进程执行和自己不一样的代码流来完成某些特定的任务,父进程本身也就是一个跑腿的,因为代码是用户写的,所以真正关心的是用户,用户需要知道子进程将自己的工作完成得怎样了
4.全局变量errno
error用于储存最后一次的错误码;
发生错误一次进程不就结束了吗,为什么会说是"最后一次"?
进程在运行过程中可能会进行多次系统调用或函数调用,并不是发生一次错误就会立即退出,所以会涉及"最后一次"的概念;
2.4库函数exit(int)
status是进程的退出状态,我们可以使用exit来以status的状态退出;
_exit(int)与exit(int)的用法是一样的,但是内部有一些区别;
exit是封装的_exit;
在exit()调用_exit之前还执行了其他的操作进行资源清理
1.执行用户通过 atexit或on_exit定义的清理函数。 2.关闭所有打开的流,所有缓存数据均被写入 3.调用_exit
所以exit比_exit多做了一层最重要的工作就是刷新缓存,我们还可以得出另一个结论就是:缓冲区绝对不在内核区!!——>因为如果在内核区的话,系统调用的_exit在终止的时候也必然会把缓冲区刷新一下,因为现代操作系统不做任何浪费时间和空间的事情,所以肯定不是由内核维护缓存区,而是由用户区在维护!!(_exit压根看不到缓冲区,所以这个工作只能有exit去完成)
一旦程序发生异常那么就程序就会直接中断,但是异常是事先知道异常的条件的,比如不能/0,一旦异常那么就不会正常接收退出码了,且退出码也不再有意义;
就比如,我们平时考试一样,考试的好不好是一回事,作弊被逮到是一回事,一旦发生那就不会有结果了;
野指针
野指针其实就是非法访问了内存空间->虚拟地址在页表中找不到,或者全是是只读,最后会转化为硬件信号给操作系统;
等待其实就是阻塞,让一个进程处于阻塞状态;
1.我们创建子进程是让子进程帮我们执行其他的任务,有的时候我们需要知道子进程的结果,然后继续执行后序的代码,这个时候就需要等待子进程完成任务后,获取子进程的退出码看看他完成的怎么样了;
2.避免僵尸进程:子进程先父进程结束会出现僵尸状态,造成进程卡死,无法回收,所以我们只需要阻塞父进程让他等待子进程完成,这样就不会出现僵尸状态了;
这里是通过两个系统调用来实现的:wait/waitpid接口;
pid_t wait(int *status);等待任意一个子进程结束; 等待成功就返回子进程的pid; 等待失败就返回-1; 如果进程没有子进程的话,就会自动返回-1;
status参数
这里的status参数是输出型参数,也就是这里只需要我们放一个变量,调用函数,自己帮我们填充这个变量的; wait函数等待成功后,status就会获得该子进程的退出码;
为什么不使用全局变量来获取子进程的退出状态呢?
因为父子进程一旦一方修改资源就会发生写时拷贝,进程具有独立性,双方看不到同一个status;
pid_t waitpid(指定子进程pid,子进程退出状态,选项); 等待成功就会返回子进程的pid,失败返回-1; waitpid可以指定等待某个子进程退出,并获取其退出状态;
输出型参数status
waitpid的输出型参数不仅可以使用一个普通整形变量;还可以使用宏定义;
(1) WIFEXITED(status) : 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) 其实等价于status&0x7F (2) WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)其实等价于(status<<8)&0xFF (3)NULL:不关心子进程的状态;
选项
0:阻塞,就是正常等待子进程退出; WNOHANG:非阻塞等待,如果执行到此语句指定子进程并没有结束,那么父进程将不会一直阻塞,而是会返回0后继续执行后面的代码,如果子进程退出了,那么返回子进程的PID;
返回值:
正常执行(没有设置WNOHANG):此时和wait一样; 等待成功就会返回子进程pid; 等待失败返回-1; 如果设置了WNOHANG 且没有可退出的子进程手机那么就会返回0;