程序和进程
程序,是指编译好的二进制文件,在磁盘上,不占用系统资源(cpu、内存、打开的文件、设备、锁…)
进程,是一个抽象的概念,与操作系统原理联系紧密,进程是活跃的程序,占用系统资源。在内存中执行。(程序运行起来,产生一个进程)
并发
并发,在操作系统中,一个时间段中有多个进程都处于已启动运行到运行完毕之间的状态。但,在一个时刻点上仍只有一个进程在运行。
时钟中断即为多道程序设计模型的理论基础。并发时,任意进程在执行期间都不希望放弃cpu,因此系统需要一种强制让出cpu资源的手段。时钟中断有硬件基础作为保障,对进程而言不可抗拒。操作系统中的中断处理函数,来负责调度程序执行。
在多道程序设计模型中,多个进程轮流使用CPU(分时复用cpu资源),而当下常见cpu为纳秒级,1秒可以执行大约10亿条指令。
CPU和MMU
cpu
程序编译成二进制文件,中间经过预处理,编译,汇编,链接四个步骤。
首先,预取器从缓冲区cache中将指令取出来。然后,然后对取出来的指令进行译码,交由译码器。对于c = 1 + 3
来讲,需要利用两个寄存器eax
和ebx
分别存放1
和3
,并将运算结果回写到eax
里面。计算过程需要交由ALU
算逻单元来进行计算。ALU
算逻单元计算完成后将计算结果放在寄存器堆中。最后,再由寄存器中将结果写入到内存中。
MMU
MMU
位于CPU内部,作为硬件存在。MMU
虚拟内存为4G
,内存结构图如下:
其中,.text
段存放代码,.data
段存放数据,heap
从下往上生长,由低地址向高地址生长。stack
的生长方向从高向低。0-3G
称为用户空间,3G-4G
称为内核区。PCB
称为进程控制块,这段空间称为虚拟内存空间。
具体一点,代码放在了.text
中,变量放在了.data
中,malloc
出来的数据放在了heap
当中。函数定义的变量和函数放在了stack
当中了。最后,内核区当中运行着程序进入到内核中的程序。
虚拟内存
是逻辑内存并不存在,仍旧在物理内存中。虚拟地址:可用的地址空间有4G。MMU的作用,1. 将虚拟地址对应到真正的物理地址中,完成虚拟内存与物理内存的映射。2. 设置修改内存访问级别。
最后,在详细描述整个流程,cpu有四种访问级别,用户空间为3级,内核空间为0级。最底层是header
段,此段为受保护区域。然后,从下向上数,第一层是.text
段,用来存放代码,为可读权限。第二层是data
段,.rodata
段仍旧是可读权限。然后是,.data
和.bss
可读写的权限,初始化为0,放在page
页是4k
。第三层是heap
段,生产方向从低到高。中间是共享库。第四层,是stack
段,生产方向由高到低。
如果代码占用2k
,在物理内存中实际占用4k
,这是因为MMU
会按照页大小进行分配。也即分配了对应物理内存为4k
的大小。
进程彼此相互独立,用户空间占用的内存地址彼此独立互不相同,但PCB
共用同一个内核空间。
总结:
MMU的作用:
- 完成物理内存和虚拟内存映射
- 修改内存的访问级别
进程控制块PCB
每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,位于内核空间。Linux内核的进程控制块是task_struct
结构体。
/usr/src/linux-headers-3.16.0-30/include/linux/shced.h
文件中可以查看struct task_struct
结构体定义,其主要成员包括:
- 进程
id
,系统中每个进程都有唯一的id
,在c
语言中用pid_t
类型表示,其实就是一个非负整数。 - 进程的状态,有就绪,运行,挂起,停止等状态。
- 进程切换时需要保存和恢复的一些
cpu
寄存器 - 描述虚拟地址空间的信息
- 描述控制终端的信息
- 当前工作目录(Current Working Directory)
umask
掩码(保存或修改文件权限)- 文件描述符表,包含很多指向
file
结构体的指针 - 和信号相关的信息
- 用户id和组id
- 会话(Session)和进程组
- 进程可以使用的资源上限(Resource Limit)
进程状态
进程基本的状态有5种,分别为初始态,就绪态,运行态,挂起态与终止态。其中,初始态为进程准备阶段,常常与就绪态结合来看。
挂起状态,等待状态实际为阻塞状态,可理解为暂停状态。