计算机系统程序员@IT·互联网

计算机系统010 - 操作系统之并行

2017-08-01  本文已影响130人  SniperPan

上一篇计算机系统009 - 操作系统之进程中讲述了操作系统设立进程、线程的概念主要是为了便于切换管理,同时也提到,在单处理器结构系统中,进行进程或线程级别的切换可以在承担一定切换消耗后降低平均响应时长。而随着硬件技术的发展,主要是多处理器架构的兴起,操作系统中并行的概念也逐渐成为主流。

既然提到多处理器架构的兴起,也就再多说几句。最开始那段时间,也就是电气CPU的年代,CPU既重又笨。第一次飞跃是随着晶体管硬件技术的革新而来,主频得到了极大提升,随后的第二次飞跃则是由集成电路所推动。发展到了这里,CPU制造商却发现通过提升主频、缓存所能换来的提升效益与成本相比,性价比很低。自然地,有一波人就打起了量的主意。通过将多个CPU核心,甚至多个CPU本身集成入芯片,同时处理多个可分割的计算任务,达到降低平均执行时长的目的。简单的来说,就是通过增加更多的服务窗口,使得排队的时间减少,进而减少整体执行时间。

1. 并行

介绍其他内容之前,先对并行、并发两个概念稍加说明。理解这两个概念时可以类比程序和进程的概念,具体如下:

插一句题外话,由于目前使用的大部分计算机系统概念都形成于西方,所使用的语言也以英文为主,汉化翻译的过程中,有时候很难找到十分贴切的词汇去映射抽象的概念本体,导致直面概念的中文词汇(如进程、线程、纤程、并行、并发)时很难获取到精确理解。这时候,还是建议先找到概念的英文表示,从英文本身的解释出发,融汇理解。

1.1 并行核心问题

回到并行本身,并行的最大特征是同时执行,那么对应的硬件前提就是切实存在多个CPU核心甚至多个CPU,以此支持线程、进程的调度。前面的章节中我们讲到,进程、线程本身是需要使用地址空间、栈空间、以及其他相关硬件资源的。按照单处理器架构执行时,始终能保证同一时刻,只会有一个进程或线程运行,所以在资源共享这一块不会出现太多问题。但在多处理器多核架构下多个进程、线程同时执行,随时有可能发生两个进程、线程需要使用同一资源(包括CPU时间、存储器、文件以及I/O设备等)的现象。

举个例子,大部分人应该都有包饺子的经历,假如现在有一碗馅料,里面只有一根勺子,那么当只有一个人在包饺子时,他可以随时获取到勺子的使用权,并在不用时将其闲置等待;假如现在还是一根勺子,但有两个人在包饺子,由于大家包饺子的节奏肯定不会绝对相同,那么在获取勺子使用权时就存在如下几种情况:

前面两种还好,既不会太闲置,也能和谐进行。但第三种情况就尴尬了,到底谁该缩回伸出去的手?虽然无比尴尬,但既然发生了问题,终归还是要解决的。从包饺子的所遇到的问题中,可以发现并行的两大核心问题是同步和通信,同步时为了互斥,通信是为了合作

1.2 同步Synchronization

同步,英文表示为Synchronization,生活中的解释是协调事件共同操作某一系统,而在计算机领域,这个同步指代两个方面的同步:

上述说明的关键点是确保事件的发生基于指定次序,万事总得讲究个先来后到,哪怕我比你早一毫秒碰到了勺子,那也是先来,理应优先响应,获得使用权。你要想获得勺子的使用权,可以先跟我确认,看我状态,看我心情给不给你用。如果我不给,那你就乖乖等着,等我用完了再用。

所以伴随着同步的是潜在互斥的结果,任何时刻,只允许一个人使用某一特定资源,比如内存中某一字节,又比如打印设备等。通常对于互斥的硬件层的支持有两种:

1.3 通信

前面提到,假如我正在使用勺子,而你希望使用,那就必须等待我使用结束,或是跟我两目对视,会心一笑让中断下来让你先用。操作系统中,进程或线程也需要通过通信来确认资源当前使用情况,有效的信息能够帮助进程及时获取资源使用权。

通常进程(线程)之间会以如下三种状态共存:

基于以上三种共存状态,进程间为了完成合作,也需要使用不同的通信机制。

2. 通信机制

对于通信机制的理解,应回归到适用的场景本身。

2.1 信号量Semaphore

在通信双方都不知道对方存在的条件下,要想完成通信,那么必须考虑通过操作系统完成同步,这一部分直接由操作系统全局统筹实现。而在双方间接知道对方的情况下,最典型的方法就是信号量。信号量的基本原理是:两个或多个进程可以通过简单的信号进行合作,一个进程可以被迫在某一位置停止,直到它接收到一个特定的信号。通过调整信号结构,理论上任何复杂的合作需求都可以得到满足。

信号量实质上可视为一个具有整数值的变量,该变量支持三个操作:

其逻辑可以换位到银行窗口服务与客户的关系,服务窗口数量就是信号量。

信号量初始值代表着wait()被阻塞前所支持的进程/线程最大数目,当该值可为任意整数时,称为计数信号量。而如果信号量初始值只能为1,也就是说信号量值只能在0/1间变化,那么该信号量也称为二进制信号量。通常的实现称为Mutex,即互斥量。和广泛分布在整个程序中的计数信号量相比,Mutex通常只用在同一线程中,即signal()/wait()均应由同一线程调用。

2.2 管程(Monitor)

管程提供和信号量相似的功能,而且在使用上更加易于控制。管程由一至多个程序、一个初始化序列以及局部数据组合而成。其主要特征是:

前两个特征与面向对象封装类似,最后一个特征则是互斥的体现。简而言之,管程就是通过分配有限的访问渠道给进程,并保证任一时刻只能由一个渠道访问数据,达到保护数据的目的。再通俗一点,管程就像是一个有多扇门的房间,房间里面储存了一些物品,任一时刻只要有人进入房间,就将所有门反锁。完成对物品的操作后,再取消反锁,退出房间。

2.3 消息传递

和现实生活中一样,消息传递的目的是为了将某些讯息告知对方,便于对方及时做出反应。进程间传递消息也是为了告知对方发生了什么事件、切换到什么状态等变化。消息要想被双方理解,就要遵循相同的格式。通用的消息格式如下图所示:

除去消息类型,最主要的字段应该是源ID和目标ID,分别代表了消息的发送者和接收者。对于同一主机而言,这可以是相互区别的进程ID即可,对于网络中的不同主机而言,该字段则需添加上网络中进行唯一标示的IP地址。

3. 典型并行问题

要想进一步理解各种通信机制,还是要回归到问题本身。并行过程中之所以会产生问题,关键在于对同一资源的竞争,如果不能协调各进程/线程有序访问该资源,就会使得依赖于资源值的进程因为值错误而导致运行错误。

3.1 生产-消费者问题

生产-消费者问题也称为有限缓冲问题,是一个经典的多线程同步问题。该问题中有如下三个主体:

问题关键在于确保生产者不会在缓冲区满时写入数据,消费者也不会在缓冲区中为空时消耗数据。换句话说,缓冲区中可用部分的多少是关键数据。

3.2 读者-写者问题

读者-写者问题发生在多个线程读或写同一共享资源时,通常,任一时刻只允许有一个线程写资源,且如有线程正在写资源,则所有读操作也会被阻塞。如无线程正在写,则多个线程可同时读取该资源。

通用的解决方案是使用读写锁,该锁中可将读与写操作互斥,而在读本身上使用信号量进行计数即可。

4. 总结

本篇概念较多,希望通过多次对比例证能够对问题进行降解。文中提及并行由来、并行过程中潜在典型问题以及相应的解决办法,但到目前为止,仍止步于可能由于对单一资源竞争引发冲突。事实上,再往坏处想,假如涉及到的资源也从单一变成了多个(如哲学家就餐问题),那么多个进程/线程与多个资源间的相互竞争则会引发更大的灾难。而这,也就是不得不单独开篇去阐述“死锁”的缘由。

上一篇 下一篇

猜你喜欢

热点阅读