多进程与多线程(二)

1.1 理解进程

书归正传,我们深入理解一下进程:首先,进程是是一个正在运行的程序的实例,此实例具有一个动态的概念,程序员书写的各种C代码、Java代码、各式脚本代码等,不具有动态的特性,所以充其量只能称为代码段,众多代码段积攒构成完整的语义整体才能被编译使用。未被操作系统调用前,其存在形式只能以静态这个词来描述。与“静”相对的,就是“动”,动静二字描述了生活中万事万物的基本状态,计算机领域很好的把现实生活中存在的概念搬迁到计算机领域里,动静结合,相得益彰。

让我们的思绪再飘远一点,讲讲静与动的关系。

程序与进程,是静与动的关系。

进程执行中,数据与正在执行的可处理数据的进程片段,是静与动的关系。

进程处理数据,数据事先给定这种方式和进程动态获取不同数据,是静与动的关系。

静与动,在计算机领域普适存在。例如:

示例需求:编写测试用例测试strcat函数。

解决方式一:测试用例写到测试程序中。

解决方式二:测试用例写到文件中,动态读取用例文件进行测试。

点评:

第一种方式把测试用例集与代码耦合,灵活性不够,修改测试用例需要重新编译代码,显然不好;第二种方式把测试用例集同代码有效分离,更为合适,从而为程序动态获取数据提供了便利支持,这里采用分别对待的思维方式,把动(变化的测试用例)与静(固定的测试程序:实现读测试用例进行测试的功能)的状态区别,有效实现解耦。

通过考虑动静的关系,区分动态和静态,极大地增加了灵活性,这,在计算机领域比比皆是,再比如,配置文件的使用;命令行参数的多端变化;函数调用的入口参数;C函数指针实现的回调函数;Java的动态类加载机制等等。

所以,当有了动态概念存在时,“进程”才得以称为进程。其实,在传统的教科书上,进程(process)没有一个明确的定义,书本只是以解释的方式描述了何为进程。每本书通常都会讲进程的特性以区分进程和程序,故而给出进程的动态性、并发性、独立性等特性,并以讲解进程在其生命周期中状态的变迁

而乐此不疲,这似乎很有理论研究的风度,把一个事物翻来覆去里里外外地研究了透,看得学习者崇拜至极,不能自拔(当然,从操作系统的角度研究如何调度进程,则进程的状态区分还是必要的)。

许多书籍(多数是操作系统相关书籍),从操作系统的角度出发讲解进程,讲了进程实例在操作系统下被抽象以后的结构组成(PCB结构、正文段、数据段;这是讲进程自我组成,尤其PCB更是为操作系统服务的),讲了进程上下文(用户级上下文、系统级上下文、寄存器上下文;这是讲进程在操作系统这个大环境下同周边环境中的软硬件的关系),讲了操作系统对其的调度方式(创建进程、撤销进程以及实现进程状态变迁),讲了进程间行为(进程间互斥与同步),讲了进程间通信(共享内存、消息、管道等)方式。但是,这种讲解,割裂了理论和实践之间的沟通道路;忽视了实际开发中,程序员对于进程知识掌握的需要;忽视了程序员需要在实践中加强对操作系统深刻认识的需要。

所以,我们从进程的创建和通信来了解一下实际编程中会碰到的一些问题。

1.1.1 进程进程的创建

作为申请系统资源的基本单位,进程必须有一个对应的物理实体。从操作系统看,这个实体叫做PCB;从应用程序看,这个物理实体叫做句柄。

对于句柄,通常,我们可依其判断进程创建是否成功、或在关闭进程的时候使用,它时,作用不大。但如果我们要为一套系统配备监控体系的时候,则需要从操作系统和进程自身内部的角度考察进程,这时,对进程的了解就可以通过进程的id(所谓的pid,俗称“进程标识符”)来标识。所以,getpid(Linux)这样的函数还是很有些意义的。

比如,对于大型数据库系统PostgreSQL,当需要对系统调优前,我们需要详细掌握各种运行状况,如进程状况、IO状况、Lock的使用状况、Buf的命中状况等。在一个可调优的数据库管理系统中,进程作为运行主体,应当被首先把握,其他如IO问题,虽然是影响性能的主要问题,但也只是局部问题。多个进程构造的复杂系统,其体系结构是否合理在现代大型复杂应用系统中越来越成为主要问题。体系结构不合理,将极大制约了系统的性能发挥,这是一个全局的问题,必须首要解决。所以,多进程、多线程结构的应用程序应用而生。在一个多进程的系统中,考察各进程的运行状况,有着重要的意义。

进程从创建到消亡,是一个时间段,在这期间,我们需要了解,这个进程在做什么事情,为此,花费了多少时间,消耗了多少资源。

进程创建,必须由另外的进程完成,这个“另外的进程”的角色,通常由操作系统完成。

C程序员编写程序,必须要有main系列函数(Windows下为main、wmain等)作为入口,这样才能被操作系统调用;Java程序员写程序也不可例外,也得有main函数;当我们在命令行输入一个可执行的程序名(或在资源管理器中双击一个可执行程序,如下道理一致),命令终端监控着输入的完成,一旦回车被键入,终端(即操作系统的一个可见代表)会判断程序是否可执行,是否有入口函数,如有,则创建PCB(进程控制块),分配内存空间给被启动的进程等等,这些工作,对应了进程的不同状态的变迁,比如在创建态发现不能给进程分配足够的内存,则必然创建进程失败。如果能创建成功,则意味着程序到进程这一过程变迁的开天辟地的大事完成,自此,程序员的才智就可以信马由缰地开跑了。

但是,如果程序只有一个main函数入口,那么世界就不会姹紫嫣红生命就不会丰富多彩了。操作系统有一个明智之举,那就是开放了进程的生命控制权给程序员,呵呵呵,这个权利的下放,确实是操作系统先哲们的一个伟大之举。这有点象上帝造人,如果上帝只捏巴出一个亚当,那么世界单调得只有亚当日出而作日落而息的枯燥生活了,为什么?“1”,亚当是“1”,一个人;一,就意味着独立意味着没有交互,没有交互就没有事端没有复杂度没有由点到点的扩展,就更不可能有由点到线到面到体的空间扩展。当夏娃诞生,生命格局就发生了伟大的变化,因为“1”变为了“2”,虽然只是多了一个“1”,但这种变迁所带来的意义是非凡的,远比由“2”变成“3”、“30”、“300000”等来的更为有意义!实际上,这是一种思维的突破,从孤立到交互,一次伟大的思维突破。人类史上,每一次思维的突破,都将为世界带来新的飞跃。这种突破,表现在编程领域,就是程序员可以自己创建控制新的进程。由此,程序员的才智就可以自由地模拟世界了。

由程序员创建控制新的进程,开创了编程的新时代,多进程协作工作成为可能。现在,大型系统的架构,好多都是架在多进程结构基础上的,比如,Oracle数据库,就是典型的多进程架构,在一个Oracle实例中,包括:DBWR,数据库写入进程;LGWR,日志写入进程;SMON,系统监控进程;PMON,进程监控进程;ARCH,归档进程;RECO,恢复进程等等,这些进程协调合作、各司其职,撑起了Oracle那浩渺的天空。

从辈分上讲,用户进程与其创建的新进程,他们具有父子关系。自古来,子承父业,子进程和父进程将生活在同一块屋檐下,同荣同贵,共享资源。但是,这种现象在操作系统是不被许可的。操作系统不是一个孙子多多的爷爷,就像狐狸在育大幼仔后,将追咬其离家出走,迫其生活走向独立一样,操作系统是一个平板系统,虽然进程可以创建子进程,但是,所有进程都是被操作系统一手管理的,他人不得染指,这就是说,操作系统将再造一个新的PCB,重新分配内存空间给子进程,以标识新的子进程,并亲自为父子进程划定势力范围,使得天下太平,父子不得相涉。但是,究竟子承不承父业呢?答案还是肯定的,子进程是可以继承父业的,但这种继承不是和父进程共享地盘(内存),而是共享父进程地盘上曾经存在的那些财富――变量、文件句柄等,共享的方式不是共同拥有,而是分身有术――复制/copy。

在子进程创建后,父子进程就分家另过,但再怎么分,也是一家人啊,他们有着千丝万缕的联系,于是,进程间通通话、发个短信什么的就很有必要了。父子间可通讯,两旁外人也得说点话啊,进程间互通音讯成为必然。

另外,父进程可以控制子进程,等待子进程结束等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值