4.2 分解以及操作系统的任务
由于以下两个原因,本书中将多次回顾分解这个主题:
软件设计的基本活动是将问题和解决方案按某种方式分解,使得解决方案(有时候是问题本身)能够在软件中加以实现。
并行编程(www.cppentry.com)、多线程和多处理都要求将软件分解为可以由操作系统调度,从而可被并发处理的执行单元。
这使得分解对于多核编程(www.cppentry.com)非常重要。注意在前面章节中所介绍的经典游戏示例中,并没有提到并行编程(www.cppentry.com)、多线程、操作系统等。有的只是对问题的简单陈述,即在5分钟或更短时间之内猜到我所想的6字符编码。我们是从对问题的简单平实的描述开始,最终以一个要求进程间通信和操作系统干预和协助的多处理、多线程程序结束。简单的描述与同时执行的操作系统线程之间的主要连接是分解的过程。应从逻辑细分和物理细分的角度来考虑示例。图4-3包含了对来自程序清单4-1和程序清单4-2的程序guess_it的操作系统组件的功能(逻辑)细分。
|
| (点击查看大图)图4-3 |
如图4-3所示,操作系统一共要负责6个执行单元,包括两个进程和4个线程。回想一下在示例4-1中,我们最初是用一个程序在一个文件中进行查找。我们使用操作系统API来产生程序的两个示例,这样就可以同时查找两个文件了。注意在图4-3中,find_code又被分成两个线程。因此,实际工作组件可以很容易地在图4-3中看到。图4-3中所没有显示出来的是将一个包含超过四百万种可能的数据文件分解为4个较小的、包含超过一百万种可能的文件。图4-3中的每个线程在自己的文件上进行查找,这样,除了对工作进行分解之外,我们还做了数据分解。程序guess_it的分解是单指令多数据(SIMD)并发模型的一个示例。在SIMD并发模型中,多个任务在不同数据集上执行相同的指令序列。我们有4个线程,每个线程在4组不同的数据上执行相同的代码(单指令)。即使我们有着工具上的优势,例如STAPL或TBB,但我们最终仍然需要同操作系统进行交互以实际实现这个SIMD模型。这种类型的分解以及操作系统接口是图4-1中所示的级别1和级别2上的编程(www.cppentry.com)。尽管在级别3和级别4(见图4-1)上工作的开发人员通常不受这种级别的交互的影响,但是应当清楚操作系统的任务。
除了涉及操作系统的逻辑单元的细分之外,还有物理单元的细分。图4-4显示了程序清单4-1和程序清单4-2中的程序的UML部署图。
|
| 图4-4 |
图4-4显示了guess_it程序的物理分片。有两个主要的可执行文件:
guess_it(程序清单4-1)
find_code(程序清单4-2)
有4个包含可能的选择的文件,以及几个包含猜测编码游戏的穷尽解决方案以及消息队列的源文件。操作系统在运行时必须知道所有二进制文件的路径。如果在程序清单4-1的第15行和第16行中的posix_spawn( )调用不能够定位到find_code程序,那么程序清单4-2中的guess_it程序将无法工作。在部署多处理和并行处理应用程序时,操作系统找到并加载待执行代码的任务经常会被忽略或以"经常出错的问题(gotchas)"的形式出现,您可以使用部署图来帮助保留应用程序物理分解的审核线索。将图4-3和图4-4中的分解、问题的最初陈述考虑、在其解决方案上的初试一并考虑,您可以开始看到SDLC如何在多核应用的设计与实现中发挥主要作用。从最初的问题陈述:"在5分钟内猜出我所想的6字符编码",我们设计了一种查找可能编码列表的解决方案。但是由于列表过大而且我们有时间约束,因此我们又提出了需要将可能编码列表分成较小的列表,这样可以同时在这些较小的列表中查找MagicCode。
这个策略是SDLC中的设计活动的示例。最初的问题陈述是SDLC中的需求定义的实例。使用posix_spawn( )、pthread_create( )和PosixQueue来对策略进行实现是SDLC中编码活动的一部分。尽管在SDLC的需求定义或设计活动中,未必需要考虑操作系统,但是在编码、部署和维护活动中,就必须考虑操作系统了。这里,我们的目的是想弄清楚它的功能在哪些方面适合需要利用多核CMP的应用程序。在第5章和第6章中,我们将详细介绍进程和线程,它们是可以被操作系统调度为同时执行的执行单元。