进程的创建
在进程的创建中,我们一个非常重要的函数 fork()函数,fork()函数会创建一个新的进程,为原有进程的子进程,原有就为父进程。
我们来看一下fork()函数的原型。
#include <unistd.h>
pid_t fork(void);
返回值:子进程返回0,父进程返回子进程的pid。fork()失败返回-1(linux下)
进程调用fork()函数后具体的操作
1)当进程调用fork()函数后,会将控制转移到内核中的fork()代码。
2)内核会分配新的内存和内核数据结构给子进程
3)将父进程部分数据结构内容拷贝到子进程中(写时拷贝)
4)添加子进程到系统的进程列表中
5)fork()返回,开始调度器调度
第一步:
调用fork()函数后到内核中去申请资源。当资源申请好就创建出子进程,子进程和父进程各自拥有独立的资源,然后把父进程的数据代码拷贝一份到子进程中。但是要注意的是:这里的拷贝是采用了写时拷贝。通常情况下,父子进程是共享代码的,数据是写时拷贝。
当有了子进程时,代码是怎么执行的。
当fork()完了子进程和父进程就从fork()函数开始向下执行,但是父进程先执行还是子进程先执行,这是取决去调度器。
虚拟地址空间简述
我们在写程序的时候每次取地址或者传指针等,这些地址都是操作系统为了物理内存更为合理的使用,虚拟出地址空间。通过虚拟地址空间。
1)能有效的分配和使用物理内存
2)能保护进程与进程之间的独立
3)同时一定程度上提高了运行速度
我们了解一下,虚拟地址与物理地址
fork()创建子进程通常在一个进程下需要做别的事情是时,可以创建一个子进程去做,自己来处理结果。例如客户端请求父进程接受,子进程去处理。
创建子进程势失败的原因
1)系统中子进程数太多达到上限
2)内存不足
vfork()函数
vfork()函数也是用来创建子进程,但是与fork()相比还是有一定的差别 。
- vfork用于创建一个子进程,而子进程和父进程共享地址空间(也就是共享页表),而fork的子进程具有独立的地址空间
- vfork是一定保证了子进程先执行,当子进程调用exec或者(_exit)之后父进程才能被执行。fork父子进程调度完全取决于调度器,在同一时刻。
当子进程改变程序中数据时候,父进程也会改变。也是说明了共享地址空间
在这里有一个我们要注意:
在vfork函数创建的子进程中,为什么用return程序会崩溃。
前面我们说的第一点,因为vfork是父子共用一块地址空间,也就是用一个页表,所以在子进程运行的过程中父进程是处于等待状态的,要是在子进程中return,那么也就是将main函数的栈帧结束了,那么当子进程结束后,父进程开始运行,但是这个时候main函数的栈帧都已经释放,所以父进程再运行就会崩溃。进程的终止
进程的终止是一个进程的结束或者说是一个进程的退出,进程的退出可以分为两种。一种是是正常的退出,一种是异常的退出。
正常退出
进程的正常退出,大概有三种,严格的说只有两种。
1)main函数的返回
2)调用系统函数或者库函数(_exit() exit() )
main()函数返回
main()函数返回,这个很好理解,当main()函数执行到return 0 时候进程也正常退出。进程也就结束。就像我们写的第一个程序hello world ,当执行程序时,就会创建出一个进程,当return后,进程也就结束。
_exit()函数
先看函数声明:#include<unistd.h> // 系统调用 void _exit(int status); // 参数status为退出码。(注意退出码为int 但是用两个字节,后面说)
_exit()函数是系统函数,用于程序的退出,一般在进程中调用_exit()会直接结束程序,不会做进程结束前的清理工作。
exit()函数
函数说明: