进程创建:
fork函数:
作用:从已经存在进程中创建一个新进程,新进程为子进程,而原进程为父进程。
执行:
-
-
- 分配新的内存块和内核数据结构给子进程
- 将父进程部分数据结构内容拷贝至子进程中
- 添加子进程到系统进程列表中
- fork返回,开始调度器调度
-
返回值:子进程返回0 父进程返回的是子进程的pid
vfork函数:
创建出来的父子进程用同一块地址空间(子进程先运行,在子进程调用exit退出或程序替换之后,父进程才退出)
(在自子进程没有运行其他程序或退出前,父进程阻塞在vfork处不会返回)
这里的子进程不能用return 退出,否则会释放内存,导致父进程调用混乱。(每一个操作系统是不一样的:Centos 下父进程会进入死循环的状态)
意义:快速创建子进程,并且子进程是专门来运行其他进程的,共用地址空间可以减少子进程数据拷贝父进程的消耗,因此速度快
但是这种函数在fork()实现了写时拷贝技术的时候就淘汰了
写时拷贝技术:
执行一个程序的时候,先将程序拷贝进物理内存(不一定连续),在创建一个父进程,与虚拟地址及页表,通过页表的映射将虚拟地址空间与物理内存连接,在创建一个子进程,以同样的方式创建了虚拟地址及页表,只不过在子进程的页表中,映射的不在是原来的程序,会为子进程重新创建一块内存拷贝原来的程序,就这样保证了数据独有。(这种拷贝只会在当你使用子进程的变量的时候才会拷贝)
fork调用失败原因:
1.系统中有太多的进程
2.实际用户的进程数超过了限制
进程终止:
方式:(库函数封装系统调用函数)
1.main函数中return
2.exit()——库函数(在退出前会刷新缓冲区,做退出收尾工作)
在退出的时候会逐步释放所有资源
在进程的任意位置调用exit都会退出进程
3._exit()——系统调用函数(不h会刷新缓冲区,直接退出,释放资源)
异常退出时:ctrl+c;信号终止
返回值:(返回值只用了一个字节来保存)
命令: echo $? (查看系统调用错误返回值)
错误原因:
每一个系统调用执行完毕后都会重置进程中的errno这么一个全局变量,这个全局变量中存储的就是当次调用的系统接口错误编号,当系统调用接口出错,用户就可以通过这个errno获取系统调用的错误原因
进程等待:等待子进程的状态改变
作用:避免僵尸进程的产生
僵尸进程产生原因:父进程不知道子进程什么时候退出,这时候子进程退处,操作系统的通知由于父进程并没有关注到,导致子进程不能被完全释放,而进入僵尸状态
必要性:
1.子进程退出,如果父进程不管,则会造成内存泄露
2.进程一旦变为僵尸进程则很难被杀死,kill -9 也不能杀
3.我们必须要获得子进程完成的如何,是否正常完成任务
4.通过等待的方式,父进程回收子进程资源,获取子进程的退出信息
调用:
wait()——阻塞函数
功能:等待任意一个子进程退出(只要有一个子进程退出就会返回)
阻塞:为了完成功能发起的调用,如果当前不具备完成条件,则一直等待,直到完成后返回
非阻塞:为了完成某个功能,如果当前不具备完成条件,则立刻报错返回
pid_t waitpid(int pid, int* statu, int opt)——opt选项参数:WNOHANG(如果没有子进程退出,则立刻把报错返回,如果有,则里回收资源)
功能:可以等待任意一个子进程退出或指定一个子进程退出
opt为0则是非阻塞
waitpid可以通过opt将操作设为非阻塞
返回值:
-1:等待任意子进程退出
>0:等待子进程退出
0:没有子进程退出或出错
获取返回值statu:
statu中保存的元素:
int 的4个字节中高16位没有使用,使用低16位:
其中高8位:
1.子进程的退出返回值:返回值储存在高 8 位中后低 8 位又分为 2 位:
2.(1b)core dump标志:程序退出时是否保存运行信息
3.(7b)异常退出信号值:正常退出值为0;
获取返回值(只有当程序正常退出去返回值才有意义):
异常信号值为0则正常退出;
(statu >> 8)& 0xff
判断程序是否正常退出:
1.statu & 0x7f
0 为正常退出
非0 为异常退出
2.WIFEXITED(statu)——宏
正常退出返回ture
WEXITSTATUS(status) 在程序退出时,获取子进程的退出码