版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
操作系统中进程管理的基本原理作者:石莹(2010-6-27)作者信箱:wondersy0618@163.com刖言文章借助王爽老师编写的Toyix操作系统,通过对一个个程序的执行结果的分析,向读者演示了操作系统中进程的基本原理和基本特性。这篇文章面向已经掌握c语言和数据结构的操作系统的初学者。读者除了学习其中的知识,更应该学习体会文章中分析探索问题的方法,并在以后的计算机学习中应用这些方法。这些学习的能力是比知识更重要的东西。文章在编写过程中参考了Toyix简易教程,《计算机操作系统》(西安电子科技大学出版社),《汇编语言》(王爽)操作系统发展的意义在没有出现操作系统以前,计算机只能在一段时间执行一段程序,程序因为IO请求等原因发生阻塞时,CPU会处于空闲状态造成资源的浪费。因为早期的计算机价格昂贵,用户就希望可以更高效的利用计算机资源。这种需求就促进了操作系统的产生和发展。进程我们今天使用的操作系统主要是分时系统,由调度程序调入的多个作业共享CPU资源,其中每个作业只执行极短的一段时间(比如0.1S,我们称为一个时间片),极短的时间过后暂停执行,调入下一个程序。这样在不长的一段时间内(比如5s内),有限的进程(少于50个)都可以得到至少一次的执行,用户请求可以得到及时的响应。这种作业调度的方式我们称为时间片轮转法。这种执行的方式叫并发执行,并发性也是分时系统的基本特性之一。在分时系统中,为了执行一项作业,就需要把要执行的作业程序载入内存中作为程序段,为作业分配相应的数据空间作为数据段,并加入一个控制块(PCB),用来保存当前作业执行所必须的一些信息,使之能够并发执行。内存中的程序段、数据段和PCB我们称为一个进程实体,而一个进程实体的执行过程我们称为进程。
进程的三种基本状态我们已经知道了进程实际是一个动态的概念,我们再回到分时系统的原理上。分时系统是给一个进程分配一个时间片,让这个进程执行,当进程时间片用完以后,为下一个进程分配时间片。当进程执行过程中发生阻塞,则主动让出CPU控制权,给其它进程执行的机会。分析上面的过程,每一时刻只有一个进程处于执行的状态。而有多个进程处于等待分配时间片的状态,这多个进程应该遵循一定的顺序。事实上是存在于一个队列中。这种等待分配时间片的状态我们称为就绪状态,存放就绪进程的队列就称为就绪队列。当CPU处于空闲状态时,调度程序就会从就绪队列中取出一个进程并执行。当进程时间片用完后,调度信息就会把这个进程放入到就绪队列中。除了时间片用完,当进程IO请求时,进程会在IO请求完毕之前无法继续执行,这类情况我们称为进程的阻塞(可能出现进程阻塞的有IO请求,申请缓冲空间等)。当出现进程阻塞后,调度程序应该怎么处理呢?首先进程肯定不能放入就绪态,因为放入就绪态就有可能被分配时间片,而此时进程不能继续执行,这就浪费了CPU资源。调度程序对其进行的处理是将其放到一个阻塞队列中,当10完成时,再把它放回就绪队列等待分配时间片。图1进程就绪态、执行态和阻塞态的关系图1图1进程就绪态、执行态和阻塞态的关系使用Toyix查看进程的三种状态Toyix是一个专门为操作系统的基础理论教学而编写的系统,通过这个系统可以很方便的模拟进程的创建执行过程。启动toyix系统,如图从Toyix网站(/)下载系统并安装(具体安装方式请参考网站教程)启动toyix系统,如图Toyix操作系统支持绝大部分常用的dos命令,我们可以使用dos下的编程方式进行编程。可以使用dos下的工具编写程序源码,如图<Co->C:>edit1-c下面我们编写一个程序,用来演示程序的三种状态#include<toyix.h>main()(inti;for(i=0;i<80;i++)(put_str(3,i,2,"a");delay(30);}get_char();for(i=0;i<80;i++)(put_str(5,i,2,"b");delay(30);}分析上面的程序,程序中引入toyix库toyix.h文件是为了使用toyix系统提供的关于的函数。程序首先在循环内调用put_str在屏幕第三行输出字符a(关于put_str的用法,请参考toyix函数手册),每输出一次延时30ms。然后调用get_char函数阻塞式获得用户输入。最后再通过一个循环在屏幕第五行输出一行字符b。下面我们在toyix下编译这个程序。toyix提供的c语言编译命令是cc,使用方式是“ccc语言源文件文件名”,如图<Co->C:>cc1-cCompiling...1.c:Auailablememovy208664Linking...Making...1-prg<Co.>C:>程序已经编译,连接成功,产生了toyix下的可执行文件2.prg,prg是toyix下可执行文件的后缀名。下面运行这个程序。Toyix中运行一个可执行文件的命令是do,使用方法是“do可知性文件的文件名”,注意不需要加后缀名,如图<Co.>C:>do1然后以后就看到了toyix程序运行时的界面blocked:ToyixMultiprocessMonitorU0.03
blocked:ToyixMultiprocessMonitorU0.03屏幕上边的蓝条是toyix进程监视器,通过它我们可以方便的知道每个进程所处于的状态。其中running后指示的是处于运行状态的进程id,ready后指示的是处于就绪状态的进程id,blocked指示的是处于阻塞状态的进程id。可以看到我们的程序已经准备完毕,按任意键开始执行。程序应该是处于就绪态,进程id是1。而此时没有更多的任务,CPU处于空闲状态,running处用一个红色的0表示。按任意键开始执行程序running:1ready: blocked: ToyixMultiprocessMonitoruO.S31.prgokHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa程序开始在屏幕输出a,此时程序处于运行态。一行输出完毕,程序会请求用户输入,如图running:0ready: blocked:1 ToyixMultiprocessMonitorU0.031.prg<15ok可以看到ID为1的进程此时处于阻塞态。按任意键,使程序继续执行running:1ready: blocked: ToyixMultiprocessMonitoru0.031.prg<1>okaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb如图,程序进入运行态继续输出字符b,知道程序运行结束。事实上,进程应该有一个进入就绪态的过程,再由操作系统取出就绪进程分配时间片后进入运行态,但是因为时间太短(Toyix系统每个时间片大概是50ms),我们没有看到那个状态。进程与轻权进程我们已经知道,操作系统的多个进程处于并发执行的状态。一个进程p1可以创建另一个进程p2,我们称p2为p1的子进程,p1为p2的父进程。每个进程都会有一个进程实体,下面我们研究父进程与子进程进程实体之间的联系我们可以应用c语言的某些特性来进行研究,比如我们可以通过下面的程序来输出一个变量在内存中的位置#include<toyix.h>inti=0;main()(printf("\n%d:%d\n",_DS,&i);} —blocked:ToyixMultiprocessMonitoru0.03执行后就得到了变量i的段地址和偏移地址(4324d:3216d)blocked:ToyixMultiprocessMonitoru0.03running:0ready4.prg<1>okPress key■■■4324:3216
也可以通过下面这段程序来打印main函数的地址#include<toyix.h>inti=0;main()(printf("\n%d:%d\n",_CS,main);} —blocked:ToyixMultiprocessMonitoru0.03执行后获得了main函数的段地址和偏移地址(4324d:0785dblocked:ToyixMultiprocessMonitoru0.03running:0ready4.prg<1>okPressemykey■■■4324:785通过以上两种方法,我们可以得到一个进程的数据和代码在内存中的位置。Toyix为我们提供了两个简单的创建进程的函数fork()和frk(),这两个函数的调用方法我们不做具体介绍,请参考toyix的函数手册。下面我们分别研究这两个函数创建进程的差别。编写下面的程序#include<toyix.h>inti=-1;main()(i=fork();printf("\n\n\nCallforkreturnvalueis%d\n",i);printf("\nPID=%d\n",get_pid());printf("\nFunctionmainoftheaddressis%d:%d\n",_CS,main);printf("\nVariableioftheaddressis%d:%d\n",_DS,&i);delay(500);}分析这个程序,这个程序调用fork()函数创建的新的进程,并在每个进程中输出fork()函数返回值(判断父进程还是子进程),当前进程ID,main函数的地址,变量i的地址。运行这个程序如图,通过fork()函数的返回值我们判断,pid为1的进程为父进程。而且父进程与子进程数据段和代码段均不相同。这说明通过fork()函数创建的进程数据与代码不共享。下面我们用同样的方法研究通过frk()函数创建的进程,修改上面的程序#include<toyix.h>inti=-1;main()(i=frk();printf("\n\n\nCallforkreturnvalueis%d\n",i);printf("\nPID=%d\n",get_pid());printf("\nFunctionmainoftheaddressis%d:%d\n",_CS,main);printf("\nVariableioftheaddressis%d:%d\n",_DS,&i);delay(500);}这个程序与上面的程序基本相同,只是把fork()函数改为了frk()函数,在toyix中运行这个程序我们发现,pid为1的进程为父进程,父进程与子进程代码段不同,但是数据段相同。这说明,通过frk()创建的进程数据段共享,代码段不共享。这种进程我们称为轻权进程。我们为什么要创建轻权进程呢?创建进程是为了提高系统资源利用率,但是因为进程拥有自己的数据段。进程的创建需要申请数据空间,销毁需要释放数据空间,切换进程需要保存并重新设置CPU寄存器现场。进程的创建,切换和销毁都需要消耗大量的CPU资源。有时我们需要把自己的一个进程分成几部分同时处理。比如程序中有一段代码会循环等待用户输入,而用户的是否输入并不会影响我们程序的继续运行,我们不希望等待用户输入时主程序也进入阻塞状态。我们可以将用户输入的部分创建一个新的进程,但是事实上这个进程并不需要分配新的数据空间。那么就出现了没有自己独立的数据空间的进程,也就是轻权进程。轻权进程在创建,切换和销毁时消耗的资源比进程都小的多。下面我们做这样一个实验。编写下面的程序#include<toyix.h>main()(frk();printf("\nPID=%d\n",get_pid());} —程序中调用frk()创建轻权进程以后每个进程打印自己的进程id。在toyix下运行并查看结果running-prg<1>ressan</ID=10readyrunning-prg<1>ressan</ID=10readyokkey-..blockedToyixMultiprocessMonitoru0.可以看到运行结果与我们预想的不太一样,只打印了一个进程的pid,另一个没有打印。这是为什么呢?我们修改一下这个程序#include<toyix.h>main()(frk();printf("\nPID=%d\n",get_pid());delay(100);与前一个比较,这个程序多调用了一个延时功能。再看运行结果runn:0ready:okkey...blockedToyixMultiprocessMonitoru0.03runn:0ready:okkey...blockedToyixMultiprocessMonitoru0.03我们发现现在的显示结果正确了。现在为什么正确了呢?我们分析第一个程序,主进程创建进程以后,打印输出自己的进程id。此时子进程还没有得到执行的机会,父进程就运行结束了,子进程也被销毁。子进程虽然创建了,但是并没有得到执行。所以出现了错误的结果。第二个程序中,在父进程打印出自己的进程id以后,通过延时给子进程执行的机会,子进程进入运行态,打印子进程pid,输出结果正常。以上分析表明,如果父进程结束,操作系统会销毁它创建的所有轻权进程。这很容易理解,因为轻权进程是没用数据空间的,它共享父进程的数据空间。而父进程销毁时,数据空间会被释放,此时如果子进程继续执行,可能访问已经释放掉的空间,这是不安全的。这一部分我们讲述了进程与轻权进程的一些差别。简单来说,进程是可以独立运行的最小单位。而轻权进程是可供操作系统调度的最小单位。后面我们要继续讲述怎样在我们编写的程序中更方便的创建多个轻权进程。使用cobegin创建轻权进程通过以上的学习我们知道,为了更高效的利用资源,我们编写的程序可能要创建多个轻权进程。但是使用frk()创建的进程只能在两个进程中执行同一段代码,这样的进程是没有实际意义的。为了方便的创建轻权进程,toyix为我们提供了cobegin函数。Toyix函数手册中对这个函数的描述如下:原型:intcobegin();功能:创建多个子进程并发执行函数参数:函数名用0结束,例如:cobegin(f1,f2,f3,0);返回:创建子进程的个数说明:所有子进程共享数据段,主进程撤销,所有子进程将被撤销。我们编写一个程序,通过cobigin函数来创建多个轻权进程。#include<toyix.h>voidf1()(printf("\nfunctionf1,pid=%d\n",get_pid());TOC\o"1-5"\h\z} —voidf2()(printf("\nfunctionf2,pid=%d\n",get_pid());} —voidf3()(printf("\nfunctionf3,pid=%d\n",get_pid());} —main()(printf("\nfunctionmain,pid=%d\n",get_pid());cobegin(f1,f2,f3,0);delay(100);}在这个程序中,main函数首先输出自己的pid,然后调用cobegin函数将f1,f2,f3函数分别作为一个轻权进程运行,在每个轻权进程里,打印自己的函数名和pid。运行结果如图多进程引发的问题下面我们要应用多进程技术编写一个模拟火车售票系统的程序。在火车票售票中心的服务器上,保存着未售出的火车票数量。而各地有很多代售点,他们在售票时会连接到售票中心检查火车票剩余情况,如果还有剩余票,那么卖出这张牌,同时通知售票中心车票被卖出。编写程序实现这个过程,首先创建一个全局变量,用来保存剩余票数。编写一个函数用来模拟一个售票窗口的售票流程。再通过在main函数中调用cobegin函数创建多个轻权进程,模拟多个窗口售票。程序代码如下:
#include<toyix.h>inttotal=10;voidf1()(while(total>0)(delay(50);printf("\npid=%d,sellticket:%d",get_pid(),total--);} —}main()(cobegin(f1,f1,0);getch();}在单个窗口的售票流程中,首先检查剩余票数是否大于0,如果大于0,那么延时一段时间(模拟真实情况),然后卖出这张票,再令剩余票减一。在toyix系统中运行,模拟结果如图running:1ready:1.prg<1>okPressanyke</.--pid=2,sellticketpid=3,sellticketpid=2,sellticketpid=3,sellticketpid=2,sellticketpid=3,sellticketpid=2,sellticketpid=3,sellticketpid=2,sellticketpid=3,sellticketpid=2,sellticketrunning:1ready:1.prg<1>okPressanyke</.--pid=2,sellticketpid=3,sellticketpid=2,sellticketpid=3,sellticketpid=2,sellticketpid=3,sellticketpid=2,sellticketpid=3,sellticketpid=2,sellticketpid=3,sellticketpid=2,sellticket019876543210blocked:ToyixMultiprocessMonitoruS.03我们发现一个问题,售票窗口售出了0号票,这在实际应用中是不允许的。为什么会发生这种情况呢?我们再分析刚才的程序,为了分析简单,我们假设假如程序运行了一段时间,还有一张票未售出。程序中每个轻权进程的执行过程如下图所示。(虚线代表阻塞态或就绪态,实现代表运行态)total:total:主进程:主进程创建完轻权进程P1和P2后,在获得用户键盘输入之前一直处于阻塞态没有获得时间片主进程:a)P1获得时间片,检测到total>0,进入售票程序延时一段时间,还未售票时间片已经用完轻权进程P1:c)P1获得时间片继续运行,售票程序售出这张票后,令total减一。再次检测total=0,循环结束,进程结束b)P2获得时间片,与a中处理过程相同轻权进程P2: id)P2获得时间片,售票后退出。但是此时total=0,所以售出了0号票此时一个进程(pid=2)检查total的值,检测到大于0进入售票程序,调用delay延时(注意票并没有售出,tatal仍为1)。此时另一个轻权进程(pid=3)获得了时间片开始运行,仍会检测tatal的值,同样进入了售票程序,调用delay延时。调度程序调出刚才的轻权进程(pid=2)的进程继续执行,售出1号票并将票数减1,再次检测tatal小于0,pid为2的轻权进程结束退出。Pid为3的轻权进程回到刚才的状态继续售票,而此时tatal为0,所以售出了0号票。显然,在这个实验中如果没有延时操作,可能不会出现这样的情况。但是在一个长期运行的多个轻权进程的系统中,很有可能出现这种两个轻权进程访问同一资源时出错的问题。增加一个延时只是放大了这种问题。通过上述分析,我们了解了一个进程通过创建多个轻权进程虽然可以提高资源的利用率,但是在访问同一资源时可能引起一些问题。为了解决这些问题,我们必须在不同轻权进程访问同一资源时进行一些判
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中建项目现金流管理培训
- 幼儿园科学活动农具
- 新疆兵团第三师图木舒克市鸿德实验学校2024-2025学年高一上学期期中考试数学试题(无答案)
- Windows Server网络管理项目教程(Windows Server 2022)(微课版)教案4 DNS服务器的配置与管理;教案5 Web和Ftp服务器的配置与管理
- 2025届河北省部分学校高三上学期11月阶段调研检测历史试题(含答案)
- 初中八年级物理上学期期中考前测试卷(人教版)含答案解析
- T-YNZYC 0113-2023 绿色药材 三七叶部病害防控技术规程
- 企业文化本源探究
- 大单元教学设计的基本步骤
- 高中语文第2单元置身诗境缘景明情2夜归鹿门歌课件新人教版选修中国古代诗歌散文欣赏
- 万千教育学前与儿童一起探索自然:幼儿园自然课程故事
- 小班美术教案:小兔家的新门帘教案及教学反思
- 人工智能在体育运动中的运用
- 心电监护技术操作并发症的预防与处理
- 储运部主管竞聘报告培训课件
- 2024再生钢铁原料
- 新媒体视听节目制作 第七章 作品的编辑构思
- 2023年康复医学治疗技术(士)考试题库汇总500道含解析836
- 后进生会议:扬起风帆向前进
- 挖掘机使用管理安全技术措施
- 机动车强制报废标准规定细则范本
评论
0/150
提交评论