操作系统相关知识整理

概念

操作系统是配置在计算机硬件上的第一层软件,是计算机系统中最基本最重要的系统软件,是对硬件系统的首次扩充。其主要目的是管理系统设备,提高它们的利用率和系统的吞吐量,为多道程序的运行提供良好的运行环境,以保证多道程序能有条不絮、高效地运行,并为用户和应用程序提供一个简单的接口,便于用户使用。

基本特征

(1)并发:并发是指宏观上一段时间内能同时运行多个程序,而并行是指同一个时刻能运行多个指令。

并行需要硬件支持,如多流水线或者多处理器。

(2)共享:共享是指系统中的资源可以被多个并发进程共同使用。

(3)虚拟:虚拟技术把一个物理实体转换为多个逻辑实体。

(4)异步:异步是指进程并不是一次性执行完毕,而是走走停停,以不可知的速度向前推进。

进程与线程

进程是对运行时程序的封装,是操作系统进行资源调度和分配的最小单位,实现了操作系统的并发;

线程是程序执行的最小单位,是CPU调度和分派的基本的单位,实现进程内部的并发。

进程是资源分配的基本单位,线程不拥有资源;

进程状态

运行状态(Runing):该时刻进程占用 CPU;

就绪状态(Ready):可运行,但因为其他进程正在运行而暂停停止;

阻塞状态(Blocked):该进程正在等待某一事件发生(如等待输入/输出操作的完成)而暂时停止运行,这时,即使给它CPU控制权,它也无法运行;

创建状态(new):进程正在被创建时的状态;

结束状态(Exit):进程正在从系统中消失时的状态;

于是,一个完整的进程状态的变迁如下图:

进程五种状态的变迁

如果有大量处于阻塞状态的进程,进程可能会占用着物理内存空间,显然不是我们所希望的,毕竟物理内存空间是有限的,被阻塞状态的进程占用着物理内存就一种浪费物理内存的行为。

所以,在虚拟内存管理的操作系统中,通常会把阻塞状态的进程的物理内存空间换出到硬盘,等需要再次运行的时候,再从硬盘换入到物理内存。

那么,就需要一个新的状态,来描述进程没有占用实际的物理内存空间的情况,这个状态就是挂起状态。这跟阻塞状态是不一样,阻塞状态是等待某个事件的返回。

另外,挂起状态可以分为两种:

阻塞挂起状态:进程在外存(硬盘)并等待某个事件的出现;

就绪挂起状态:进程在外存(硬盘),但只要进入内存,即刻立刻运行;

这两种挂起状态加上前面的五种状态,就变成了七种状态变迁(留给我的颜色不多了),见如下图:

导致进程挂起的原因不只是因为进程所使用的内存空间不在物理内存,还包括如下情况:

通过 sleep 让进程间歇性挂起,其工作原理是设置一个定时器,到期后唤醒进程。

用户希望挂起一个程序的执行,比如在 Linux 中用 Ctrl+Z 挂起进程;

进程控制块(process control block,PCB)

PCB 是进程存在的唯一标识,这意味着一个进程的存在,必然会有一个 PCB,如果进程消失了,那么 PCB 也会随之消失。

它包括

进程描述信息:

进程标识符:标识各个进程,每个进程都有一个并且唯一的标识符;用户标识符:进程归属的用户,用户标识符主要为共享和保护服务;

进程控制和管理信息:

进程当前状态,如 new、ready、running、waiting 或 blocked 等;进程优先级:进程抢占 CPU 时的优先级;

资源分配清单:

有关内存地址空间或虚拟地址空间的信息,所打开文件的列表和所使用的 I/O 设备信息。

CPU 相关信息:

CPU 中各个寄存器的值,当进程被切换时,CPU 的状态信息都会被保存在相应的 PCB 中,以便进程重新执行时,能从断点处继续执行。

进程的组织

通常是通过链表的方式进行组织,把具有相同状态的进程链在一起,组成各种队列。比如:

将所有处于就绪状态的进程链在一起,称为就绪队列;

把所有因等待某事件而处于等待状态的进程链在一起就组成各种阻塞队列;

另外,对于运行队列在单核 CPU 系统中则只有一个运行指针了,因为单核 CPU 在某个时间,只能运行一个程序。

详见 https://baijiahao.baidu.com/s?id=1672062183983141715&wfr=spider&for=pc

协程

协程是一种程序组件,是一种用户态的轻量级线程。执行过程更类似于子例程,或者说不带返回值的函数调用。

一个程序可以包含多个协程,可以对比与一个进程包含多个线程

协程多与线程进行比较

1) 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。

2) 线程进程都是同步机制,而协程则是异步

3) 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态

死锁

在两个或多个并发进程中,如果每个进程持有某种资源而又都等待别的进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁,也就是多个进程无限期的阻塞、相互等待的一种状态。

死锁的四个必要条件:

(1)互斥条件:一个资源每次只能被一个进程使用。此时若有其他进程请求该资源,则请求进程只能等待。

(2)请求与保持条件:进程已经获得了至少一个资源,但是又提出新的资源请求,而该资源已经被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。即当一个进程等待其他进程是,继续占有已经分配的资源。

(3)不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,只能由获得该资源的进程释放。

(4)循环等待条件:若干进程间形成首尾相接的循环等待资源的关系。

死锁的检测

1、画出资源分配图

系统死锁,可利用资源分配图来描述。如下图所示,用长方形代表一个进程,用框代表一类资源。由于一种类型的资源可能有多个,用框中的一个点代表一类资源中的一个资源。从进程到资源的有向边叫请求边,表示该进程申请一个单位的该类资源;从资源到进程的边叫分配边,表示该类资源已经有一个资源被分配给了该进程。

2、简化资源分配图

第一步:先看A资源,它有三个箭头是向外的,因此它一共给进程分配了3个资源,此时,A没有空闲的资源剩余。

第二步:再看B资源,它有一个箭头是向外的,因此它一共给进程分配了1个资源,此时,B还剩余一个空闲的资源没分配。 

第三步:看完资源,再来看进程,先看进程P2,它只申请一个A资源,但此时A资源已经用光了,所以,进程P2进入阻塞状态,因此,进程P2暂时不能化成孤立的点。 

第四步:再看进程P1,它只申请一个B资源,此时,系统还剩余一个B资源没分配,因此,可以满足P1的申请。这样,进程P1便得到了它的全部所需资源,所以它不会进入阻塞状态,可以一直运行,等它运行完后,我们再把它的所有的资源释放。相当于:可以把P1的所有的边去掉,变成一个孤立的点,如下图所示:

第五步:进程P1运行完后,释放其所占有的资源(2个A资源和1个B资源),系统回收这些资源后,空闲的资源便变成2个A资源和1个B资源,由于进程P2一直在申请一个A资源,所以此时,系统能满足它的申请。这样,进程P2便得到了它的全部所需资源,所以它不会进入阻塞状态,可以一直运行,等它运行完后,我们再把它的所有的资源释放。相当于:可以把P2的所有的边都去掉,化成一个孤立的点,变成下图: 

(若能消去图中所有的边,则称该图是可完全简化的,如上图)

3、使用死锁定理判断

死锁定理: 
                     ①如果资源分配图中没有环路,则系统没有死锁; 
                     ②如果资源分配图中出现了环路,则系统可能有死锁。 
或者说: 
当且仅当S状态的资源分配图是不可完全简化的时候,系统状态则是死锁状态

详见 https://blog.csdn.net/jgm20475/article/details/81297819

避免死锁

操作系统锁的种类

1、读写锁(Read-Write Lock)

读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。

这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。
在读写锁保持期间也是抢占失效的。
如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里,直到没有任何写者或读者。如果读写锁没有写者,那么读者可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该读写锁。

2、互斥锁(互斥量) (Mutex)

在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。在Posix Thread中定义有一套专门用于线程同步的mutex函数。

3、信号量(Semaphore)

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。

以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

4、临界区(Critical Section)

每个进程中访问临界资源的那段代码称为临界区(Critical Section)(临界资源是一次仅允许一个进程使用的共享资源)。每次只准许一个进程进入临界区,进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。

进程进入临界区的调度原则是:
1、如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。
2、任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。
3、进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。
4、如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。

5、条件变量(Condition Variable)

条件变量使我们可以睡眠等待某种条件出现。
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

详见 http://www.pinlue.com/article/2020/06/2300/1110789836713.html

原文地址:https://www.cnblogs.com/Annetree/p/13511724.html