进程概念:进程就是正在执行的一个程序;(哲学)
正确理解进程概念:操作系统主要是一款搞管理的软件,他通过驱动程序间接管理硬件,当然,他也会管理进程,既然是管理,那么我们需要先描述,然后在组织。描述进程,进程信息被放在一个进程控制块的数据结构中,可以吧进程控制块理解为进程属性的集合,其实这个进程控制块就是PCB,在Linux中就是task_struct。在这个阿结构体内部就是详细对进程的一些描述,主要包括以下信息:
1. 标识符:要区分每一个进程,所以每一个都有唯一的一个进程id;
2. 辅助操作系统进行进程调度的属性
(1)优先级
(2)程序计数器(EPI):会记录当前程序执行到哪;
(3)进程的状态:包括运行状态R、睡眠状态S、僵尸状态Z、磁盘休眠状态D等
运行状态R:也不一定在运行,可能是在运行队列里,因为cpu采取并发的方式进行调度进程(补充概念:并发:多个进程在一个cpu下采取进程切换的方式进行运行,因为cpu运行速率极快,所以可以视为同一时间内完成多个进程的调度。并行:多个进程在多个cpu下同时运行)
睡眠状态S:也称为可中断睡眠,在等待某一进程结束;
僵尸状态Z:僵尸状态形成原因就是子进程结束了,可是父进程没有对其做出相应的处理;
磁盘休眠状态D:也称为不可中断睡眠,一般在等待I/O的结束
(4)上下文:把进程上次在cpu内部执行的现场保存在寄存器内部;
(5)记账信息:包括处理器时间总和,使用的时钟总和等;
3. I/O相关信息:打开文件返回值文件描述符;
fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。于是起初我就感到奇怪,子进程的物理空间没有代码,怎么去取指令执行exec系统调用呢?!原来在fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。在网上看到还有个细节问题就是,fork之后内核会通过将子进程放在队列的前面,以让子进程先执行,以免父进程执行导致写时复制,而后子进程执行exec系统调用,因无意义的复制而造成效率的下降。
详细情况可以看看这位仁兄的文章:十辨十析之辨一――fork()、写时复制、vfork()
这三个方法是大佬L用 于创建子进程的三种方法。其实理解它们,要有一个实现上的意识即:进程->虚拟地址空间->物理地址空间即真实的存储。用户进程能感知的是进程的虚拟地址 空间,而虚拟地址空间->物理地址空间则由底层的内核来帮你实现。一个进程在地址空间上的表现形式就是:正文段,数据段,堆,栈。嗯,主要就是这四个部 分,内核为其分配相应的数据结构来表示它们,其看做是进程在地址空间的实体,也可以想象为灵魂。随后内核会为这四部分分配相应的载体,即真正的物理存储, 就像灵魂要附之于身体一样,那么,这些物理存储就是进程的真正实体的,我们称之为身体。那么这三个方法有什么不同呢?
ok,现在有一个父进程P1,这是一个主体,那么它是有灵魂也就身体的哦。现在在其虚拟地址空间(有相应的数据结构表示)上有:正文段,数据段,堆,栈这四个部分,相应的,内核要为这四个部分分配各自的物理块。即:正文段块,数据段块,堆块,栈块。至于如何分配,这是内核去做的事,在此不详述。
1、fork*
现在P1用fork()函数为进程创建一个子进程P2,内核:(1)复制P1的正文段,数据段,堆,栈这四个部分,注意是其内容相同。(2)为这四个部分分配物理块,P2的:正文段->PI的正文段的物理块,其实就是不为P2分配正文段块,让P2的正文段指向P1的正文段块,数据段->P2自己的数据段块(为其分配对应的块),堆->P2自己的堆块,栈->P2自己的栈块。
2、写时复制
写时复制技术:内核只为新生成的子进程创建虚拟空间结构,它们来复制于父进程的虚拟究竟结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间。
3、vfork
vfork():这个做法更加火爆,内核连子进程的虚拟地址空间结构也不创建了,直接共享了父进程的虚拟空间,当然了,这种做法就顺水推舟的共享了父进程的物理空间。
通过以上的分析,相信大家对进程有个深入的认识,它是怎么一层层体现出自己来的,进程是一个主体,那么它就有灵魂与身体,系统必须为实现它创建相应的实体, 灵魂实体与物理实体。这两者在系统中都有相应的数据结构表示,物理实体更是体现了它的物理意义。呵呵,说了这么多,其实系统之所以提供这三个方法,也都是 从实现效率上来考虑的,一般fork后要exec,所以很多父进程的数据对于子进程来说都是不需要的,后两种方法就是大佬L区别于Unix的一个主要特征,也可以说是其高明处之一吧,其创建进程特别的高效,怎么高效,通过以上的比较与分析,相信大家也能明白个五六了吧。