前言:
现代操作系统这本书,也是列入了我的书单,早就打算读了,结果发现里面的内容有些讲的太细了,因此有些觉得暂时难以接触的部分是非常快速的阅读过去的。即使这样,读完这本书还是花了我一个月的时间,经典的书籍确实需要细读。
现在书其实是已经读完了,现在来写博客,目的是再巩固一次所学的知识,毕竟这些底层的内容大部分时候是不用的,也容易忘记。
我的github:
我实现的代码全部贴在我的github中,欢迎大家去参观。
https://github.com/YinWenAtBIT
进程:
一、定义:
在进程模型中,计算机上所有可以运行的软件,通常包括操作系统,被组织成若干顺序进程,简称进程。在UNIX中,进程和它所有的子女以及后羿共同组成一个进程组,当用户从键盘发出一个信号时,该信号被送给与该键盘相关的进程组里的所有成员。
二、进程的状态与实现:
1. 进程的状态:
a. 运行态:该进程此刻正实际占用CPU
b. 就绪态:可运行,但是因为其他进程正在运行而暂停终止
c. 阻塞态:除非某外部事件发生,否则该进程不能运行
其中,阻塞态只能转化成就绪态,就绪太只能转化成运行态。
2. 进程的实现:
为了实现进程模型,操作系统维护着一张表格,即进程表。
每个进程占用一个进程表项,该表项包含了进程状态的总要信息:
a. 程序计数器
b. 堆栈指针
c. 内存分配状况
d. 所打开文件状态
e. 账号和调度信息
f. 运行态转变为阻塞或者就绪态必须保持的信息
这样,进程就能保证再次启动,就好像从来没有被中断过一样。
线程:
一、经典线程模型:
1. 理解进程的一个角度是,用某种方法把相关的资源集中在一起。进程又乘法程序正文和数据以及其他资源的地址空间。
2.进程拥有一个执行线程,通常简写为线程,在线程中有一个程序计数器,用来记录接着要执行哪一条指令,线程拥有计数器,自己的堆栈。多个线程共享一个地址空间和其他的进程资源。
线程可以共享进程上述的所有资源。
3. 线程同样可以处于四种状态:
运行、阻塞、就绪、终止。
4. 线程可以通过调用pthread_exit退出或者pthread_yield放弃使用CPU
二、用户空间中实现线程:
1. 通用结构:
内核在这里只管理进程,因此只有进程表,没有线程表。线程表由进程自行管理。
2.线程表:
线程表与进程表类似,不过它仅仅记录各个线程的属性,如线程的程序计数器,堆栈指针,寄存器,状态等等。
3. 线程切换:
由于线程切换是在用户控件进行的,因此切换的速度要比内核级线程快一个数量级。这是使用用户级线程包的极大优点。
另一个优点就是用户级线程可以制定自己的调度算法。
二、在内核中实现线程:
1.通用模型
该模型中,内核拥有线程表,保存了每个线程的信息。
3. 混合实现:
有一种混合的实现方式,多个用户线程对应一个内核线程。
进程间通信:
一、竞争条件:
两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序,称为竞争条件。
二、临界区:
1.定义:
我们把对共享内存进行访问的程序片段称为临界区。
2. 避免竞争条件的要求:
a. 两个进程不能同时处于临界区
b. 不应该对CPU 的速度和数量做出任何假设
c. 临界区外运行的进程不得阻塞其他进程
d. 不得使进程无限期等待进入临界区
三、互斥方案:
1. 屏蔽中断:
进程在进入临界区之后立刻屏蔽所有中断,在离开之前打开所有中断。这个方案不好,因为把屏蔽中断的权利交给用户不明智,并且在多处理器情况下,只屏蔽该进程使用的CPU并没有用。
2. 锁变量:
假设有一个共享变量,进程在进入之前测试变量是否为0,为0则进入并设置变量为1。
该方法不能保证一个进程测试结束之后被抢占,另一个进程同时测试的情况出现。
3.严格轮换法:
两个进程测试同一个变量,一个在变量为0时进入临界区,另一个在变量为1时进入临界区。这样的方式为忙等待。该方式浪费CPU时间,通常应该避免。用于忙等待的锁,称为自旋锁。
4. Peterson解法:
使用了一个巧妙的方式,完成了进程使用自己的参数进入临界区,并且发送冲突时,后进入的进程有效的方式。
5. TSL指令:
该指令测试并加锁,该字的读写是不可分割的,指令结束之前,其他的处理器不能访问该内存字。
四、生产者消费者问题:
该问题是两个进程共享一个公共的固定大小的缓冲区。其中一个是生产者,将消息放入缓冲区,另一个是消费者,将消息取出缓冲区。
五、信号量:
1. 信号量定义:
信号量的取值可以是0或者正值,检查数值,修改变量值以及可能发生的睡眠操作均作为单一的,不可分割的原子操作。
2. 使用信号量来解决生产者消费者问题:
使用三个信号量,即可。
3. 互斥量:
互斥量是信号量的简化版本,只有一个数值。
六、条件变量
线程中的同步,除了使用信号量,互斥量以外,还可以使用条件变量。
1.使用:
条件变量与互斥量经常一起使用,先让一个线程锁住一个互斥量,然后当无法获得所需的结果时,等待一个条件变量。此时被锁住的互斥量将被解锁。然后另一个线程锁住互斥量,完成它的操作之后给条件变量发送一个信号,这个线程释放它锁住的互斥变量后,条件变量将会获得互斥锁。
2.注意:
条件变量不会存在内存中。如果一个信号量传递给一个没有线程在等待的条件变量,那么这个信号就会丢失,需要小心避免丢失信号量。
3. 管程:
这是一种高级同步原语,它由过程,变量,以及数据结构组成一个集合。但是大多数编程语言中没有管程这个概念。
调度:
一、调度介绍
1. 定义:
几个进程处于就绪状态时,需要选择一个运行,完成选择工作的这一部分就叫做调度。
2. 进程行为:
在计算上花费了绝大多数时间的进程称为计算密集型,
在等待IO上花费了绝大多数时间的称为IO密集型。
二、何时调度
1.在创建新进程后,调度父进程还是子进程
2. 一个进程退出后必须做出调度决策
3. 当一个进程在IO或者信号量或者其他原因阻塞时,选择另一个进程运行。
4. IO中断时,必须进行调度。
三、调度算法分类
1.批处理:
批处理主要用于周期性作业
a,先来先服务:
该算法是最简单非抢占式算法,先来先服务,按照任务提交的顺序来使用CPU,遇到进程阻塞时,进程被移动到列表最后一个。
b.最短作业优先:
运行时间可以预知时才可以使用此算法
c. 最短剩余时间优先:
该算法是可以抢占版本的最短作业优先算法,当新来的任务比当前运行的任务剩余时间短时,新任务可以抢占运行。
2. 交互式系统中的调度:
a. 轮转调度:
每个进程被分配一个时间段,被称为时间片。时间片通常设置在20-50ms之间。
b. 优先级调度:
优先级高的进程可以先运行,正在运行的程序会随着运行优先级降低。
c. 多级队列
d. 最短进程优先:
e.保证调度
f. 彩票调度
3. 实时系统中的调度:
外部的一种或者多种物理设备给了计算机一个刺激,计算机必须在一个确定的时间范围内做出反应。
实时系统又可以分成硬实时和软实时。
四、调度策略与机制:
将调度策略与调度机制分开,调度算法以某种方式参数化,而用户来填写这些参数。
在实时系统中,乱转调度和优先级调度更为常用
经典IPC问题:
一、哲学家就餐问题:
需要考虑死锁,以及是否能达到最大就餐人数的问题。
具体的解法很精妙,再此处不介绍
二、读写者问题:
该问题有两个版本,一个是写着优先,一个是读者优先。解决问题的办法是使用互斥量来约束读者写着进入使用资源,以及限制读写者进入队列排队的方式。
总结:
进程和线程之间的区别弄清楚之后,还需要仔细了解其可以访问的资源。以及解决他们之间的通信,调度问题。再次之上又有经典的生产者消费者问题,哲学家就餐问题,读写者问题。理解了这些问题之后,才能真正的算对进程线程有了深入的理解。