- 进程/内核模式:
CPU既可以在运行在用户态下,也可以运行在内核态下。实际上,一些CPU可以有两种以上的执行状态。
例如,Intel 80*86微处理器有四种不同的执行状态。但是,所有标准的Unix内核都仅仅利用了内核态和用户态。
当一个程序在用户态下执行时,它不能直接访问内核数据结构或者内核的程序。
然而,当应用程序在内核态下运行时,这些限制不再有效。
每种CPU模型都为从用户态到内核态的转换提供了特殊的指令,反之亦然。一个程序执行时,大部分时间都是在用户态下,只有需要内核提供的服务时才会切换到内核态。当内核满足了用户程序的请求后,它让程序又回到了用户态下。
内核本身不是一个进程,而是进程的管理者。进程/内核模式假定:请求内核服务的进程使用系统调用的特殊编程机制。每个系统调用都设置了一组识别进程请求的参数,然后执行与硬件相关的CPU指令完成从用户态到内核态的转换。
- 进程实现:
为了让内核管理进程,每个进程由一个进程描述符表示,这个描述符包含有关进程当前状态的信息。
当内核暂停一个进程的执行时,就把几个相关处理器寄存器的内容保存在进程描述符中。这些寄存器包括:
* 程序计数器(PC)和栈指针(SP)寄存器
* 通用寄存器
* 浮点寄存器
* 包含CPU状态信息的处理器控制寄存器(处理器状态字,Processor Status Word)
* 用来跟踪进程对RAM访问的内存管理寄存器
当内核决定恢复执行一个进程时,它用进程描述符中的合适的字段来装载CPU寄存器。因为PC中所存的值指向下一条将要执行的指令,所以进程从它停止的地方恢复执行。
当一个进程不在CPU上执行时,它正在等待某一个事件。Unix内核可以区分很多等待状态,这些等待状态通常由进程描述符队列实现。每个(可能为空)队列对应一组等待特定事件的进程。
- 可重入内核:
所有的Unix内核都是可重入的,这意味着若干个进程可以同时在内核态下执行。当然在单个CPU系统上,只能有一个进程在执行,但是有许多进程可能在等CPU或者某一个IO操作完成时在内核态下被阻塞。
提供可重入的一种方式是编写函数,以便这些函数只能修改局部变量,而不能改变全局变量数据结构,这样的函数叫做可重入函数。但是可重入内核不仅仅局限于这样的可重入函数(尽管一些实时内核正是如此实现的)。相反,可重入内核可以包含非重入函数,并且利用锁机制保证一次只有一个进程执行一个非重入函数。
如果一个硬件中断发生,可重入内核能挂起当前正在执行的进程,即使这个进程处于内核态。这种能力非常重要:能够提高发出中断的设备控制器的吞吐量。一旦设备已发出一个中断,它就一直等待直到CPU应答为止。如果内核能够快速应答,并且利用控制器在CPU处理中断时就能执行其他任务。
- 进程地址空间:
每个进程运行在它的私有地址空间。
在用户态下运行的进程涉及到私有栈、数据区和代码区。当在内核态运行时,进程访问内核态的数据区和代码区,但使用另外的私有栈。
因为内核是可重入的,因此几个内核控制路径(每个都与不同进程相关)可以轮流执行。在这种情况,都引用自己的私有栈。
在一些情况下,可以共享部分地址空间,可以由进程显示提出,也可以由内核自动完成以节约内存。
- 同步临界区:
非抢占式内核–>禁止中断–>信号量–>自旋锁–>避免死锁
信号量仅仅是与一个数据结构相关的计数器。所有内核线程在试图访问这个数据结构之前,都要检查这个信号量。可以把每个信号量看成一个对象,其组成如下:
* 一个整数变量
* 一个等待进程的链表
* 两个原子方法:down()和up()
down方法对信号量减1,如果这个新值小于0,该方法就把正在运行的进程加入到这个信号量链表,然后阻塞该进程(即调用调度程序)。up方法对信号量加1,如果这个新值大于或者等于0,则激活这个信号量链表中的一个或者多个进程。
自旋锁,在多CPU系统中,信号量并不总是解决同步问题的最佳方案。为了检查信号量,内核必须把进程插入到信号量链表中,然后挂其它。因为这两个操作比较费时,完成这些操作时,其他的内核控制路径可能已经释放了信号量。
在这些情况下,多处理器系统使用了自旋锁。自旋锁和信号量相似,但没有进程链表;当一个进程发现锁被另外一个进程锁着时,它就不停地“旋转”,执行一个紧凑的循环指令直到锁打开
自旋锁在单处理器环境无效的。
**
- 信号和进程间的通信:
**
Unix信号(singal)提供了把系统事件报告给进程的一种机制。
每个事件都有一个信号编号,通常用一个符号常量来表示。
POSIX标准定义了大约20种不同的信号,其中,有两种是用户自定义的,可以当作用户态下进程通信和同步的原语机制。一般来说,进程可以以两种方式对接受到的信号做出反应:
1.忽略该信号
2.异步地执行一个指定的过程(信号处理程序)
如果进程不指定选择何种方式,内核就根据信号的编号执行一个默认操作。五种可能的默认操作是:
* 终止进程
* 将执行上下文和进程地址空间的内容写入一个文件(核心转储,core dump),并终止进程
* 忽略信号
* 挂起进程
* 如果进程曾被暂停,则恢复它的执行。
因为POSIX语义允许进程暂时阻塞信号,因此内核信号的处理相当精细。此外,SIGKILL和SIGSTOP不能直接让进程处理,也不能由进程忽略。
* 进程管理:
待补充。。。。
参考《深入理解Linux内核》