合肥师范学院第二学期操作系统实验指导书_第1页
合肥师范学院第二学期操作系统实验指导书_第2页
合肥师范学院第二学期操作系统实验指导书_第3页
合肥师范学院第二学期操作系统实验指导书_第4页
合肥师范学院第二学期操作系统实验指导书_第5页
已阅读5页,还剩27页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、操作系统实验指导书操作系统实验指导书操作系统课程组编著计算机科学与技术系目 录前言- 1 -实验项目一 熟悉LINUX基本命令及编程环境- 2 -实验项目二 进程管理- 4 -实验项目三 进程调度- 9 -实验项目四 进程通信- 15 -实验项目五 存储管理- 24 -前言操作系统是计算机本科各专业的专业核心课程,其实践性、应用性很强,实验教学环节是必不可少的一个重要环节。通过操作系统实验部分教学,使学生加深理解和更好的掌握操作系统的基本原理、技术和方法,巩固所学理论知识,激发实验兴趣,掌握实验要领,培养对操作系统理论课程所学知识融会贯通和综合运用的能力。通过实验,使学生深入了解和熟练掌握Li

2、nux操作系统的使用,及在Linux操作系统下进行程序设计开发的方法,掌握操作系统中进程管理、进程调度、进程通信和存储管理的方法,使学生具有初步分析实际操作系统的能力,为今后学习使用其它的程序设计环境和语言打好基础。为了收到良好的实验效果,编写这本实验指导书。在指导书中,每一个实验均按照该课程实验大纲的要求编写,力求紧扣理论知识点、突出设计方法、明确设计思路,通过多种形式完成实验任务,最终引导学生有目的、有方向地完成实验任务,得出实验结果。实验前,指导教师布置实验任务,给定实验内容,进行一定的分析和讲解,学生进行预习,提前设计实验方案,之后进入实验室进行实验;实验中,要求学生按照实验要求进行实

3、验,认真完成每个实验项目的具体内容,指导教师全程指导协调实验进行,对于实验中学生所提问题进行具体解答;实验后,学生应当及时总结实验过程,并按照实际情况对实验报告进行填写,能对在实验过程中发生的问题及时分析并找到解决方案,提交实验报告;指导教师需要对实验报告进行认真批阅,并根据需要选取重点内容进行点评分析。实验项目一 熟悉LINUX基本命令及编程环境一、 实验类型本实验为验证性实验。二、 实验目的与任务1. 熟悉Linux操作系统的安装和使用;熟悉使用Linux字符界面,窗口系统的常用命令。2. 掌握运用Linux常用的编程工具;掌握如何编辑、编译、运行程序。三、 实验准备1. 熟悉linux系

4、统中常用命令及其功能2. 熟悉vi编辑器或Gedit编辑器的各项功能3. 复习C语言程序的编写。四、 实验内容1. 练习使用Gedit编辑器使用Gedit编辑器用C语言编写一个HelloWorld程序,并保存。具体操作:点击“任务栏位置主文件夹”,打开主文件夹位置文件浏览器,空白处右键单击,弹出菜单选择“创建文档空文件”,新建一个空文件,并命名为“hello.c”,右键单击“hello.c”,选择“使用Gedit打开”,在Gedit编辑器中编辑代码如下:#include<stdio.h>int main() printf("Hello,Wrold!n");编辑完

5、成后,点击“保存”,保存文件。2. 使用gcc编译源程序。gcc是linux下的一种c程序编译工具,使用方法如下:编译: gcc -o filename1 filename.c(或者gcc filename.c -o filename1),其中: filename.c是源文件名,filename1 是目标文件名,o代表object具体操作:点击“任务栏应用程序附件终端”,当前默认路径即为主文件夹,输入“gcc hello.c -o hello”,回车运行后,若无任何提示,怎说明编译成功,已生成可执行文件“hello“,若提示有错误,则根据具体提示回到Gedit中修改源程序,保存后重新编译。3.

6、 执行程序执行:./filenamel 其中: filename1 是目标文件名。具体操作:在“终端”中输入“./hello”,回车后运行,若无错误,终端中将显示运行结果“Hello,Wrold!”。五、 注意事项1. gcc编译器不能编译不带扩展名的c语言程序。2. 注意编译和运行程序的基本过程。3. 重新编辑源程序后,必须重新编译,才会生成新的可执行程序。实验项目二 进程管理一、 实验类型本实验为验证性实验。二、 实验目的1. 理解进程的概念,掌握父、子进程创建的方法。2. 认识和了解并发执行的实质,掌握进程的并发及同步操作。三、 实验预备知识1. fork()函数头文件:#include

7、<unistd.h>#include<sys/types.h>函数原型:pid_t fork( void);(pid_t 是一个宏定义,其实质是int,被定义在#include<sys/types.h>中)返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1函数说明:一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。子进程是父进程的副本,它将获得父进程

8、数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。linux将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。为什么fork会返回两次?由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。因为fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的,过程如图2.1。调用fork之后,数据、堆栈有两份,代码仍然为一份但是这个代码段成为两个进程的共享代码段都从fork函数中返回,箭头表示各自的执行处。当父子进程有一个想要修改代码段时,两个进程真正分裂。图

9、2.1 fork()函数分裂示意图示例代码:#include<sys/types.h> /对于此程序而言此头文件用不到#include<unistd.h>#include<stdio.h>int main(int argc, char * argv )int pid = fork();if (pid < 0)printf("error!");else if( pid = = 0 )printf("This is the child process!&qu

10、ot;);else printf("This is the parent process! child process id = %d", pid);return 0;fork函数的特点概括起来就是“调用一次,返回两次”,在父进程中调用一次,在父进程和子进程中各返回一次。fork的另一个特性是所有由父进程打开的描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。2. wait()函数头文件:#include<sys/types.h>#include<sys/wait

11、.h>函数原型:pid_t wait (int * status);返回值:如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。函数说明:wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则参数status 可以设成NULL。子进程的结束状态值请参考下面的waitpid()。示例代码:#include<stdlib.h>#include<unistd.h&g

12、t;#include<sys/types.h>#include<sys/wait.h>int main(int argc, char * argv)pid_t pid;int status,i;if(fork()= =0)printf("This is the child process .pid =%dn",getpid();exit(5);elsesleep(1);printf("This is the parent process ,wait for child.n");pid=wait(&am

13、p;status);i=WEXITSTATUS(status);printf("childs pid =%d .exit status=%dn",pid , i);3. waitpid()函数头文件:#include<sys/types.h>#include<sys/wait.h>函数原型:pid_t waitpid(pid_t pid,int * status,int options);返回值:如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。函数说明:waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在

14、调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status 可以设成NULL。参数pid 为欲等待的子进程识别码,其他数值意义如下:pid<-1 等待进程组识别码为pid 绝对值的任何子进程。pid=-1 等待任何子进程,相当于wait()。pid=0 等待进程组识别码与目前进程相同的任何子进程。pid>0 等待任何子进程识别码为pid 的子进程。参数option 可以为0 或下面的组合:WNOHANG 如果没有任何已经结束的子进程则马

15、上返回,不予以等待。WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。子进程的结束状态返回后存于status,底下有几个宏可判别结束情况:WIFEXITED(status)如果子进程正常结束则为非0 值。WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真。WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。WIFSTOPPED(stat

16、us)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。4. exit()函数头文件:#include< stdlib.h >函数原型:void exit(int status);返回值:无。函数说明:进程结束正常终止,返回结束状态。status为进程结束状态,是返回给父进程的一个整数,以备查考。为了及时回收进程所占用的资源并减少父进程的干预,UNIX/LINUX利用exit( )来实现进程的自我终止,通常父进程在创建子进程时,应在

17、进程的末尾安排一条exit( ),使子进程自我终止。exit(0)表示进程正常终止,exit(1)表示进程运行有错,异常终止。如果调用进程在执行exit( )时,其父进程正在等待它的终止,则父进程可立即得到其返回的整数。核心须为exit( )完成以下操作:(1)关闭软中断(2)回收资源(3)写记帐信息(4)置进程为“僵死状态”四、 实验内容1. 编写一C语言程序,实现在程序运行时通过系统调用fork( )创建两个子进程,使父、子三进程并发执行,父亲进程执行时屏幕显示“I am father”,儿子进程执行时屏幕显示“I am son”,女儿进程执行时屏幕显示“I am daughter”。2.

18、 多次连续反复运行这个程序,观察屏幕显示结果的顺序,直至出现不一样的情况为止。记下这种情况,试简单分析其原因。3. 修改程序,在父、子进程中分别使用wait()、exit()等系统调用“实现”其同步推进,并获取子进程的ID号及结束状态值。多次反复运行改进后的程序,观察并记录运行结果。五、 实验报告要求1. 列出实验内容1、3各程序清单,并以截图形式记录相应运行结果。2. 对实验运行结果进行分析:1)实验内容1运行结果为什么无固定顺序,fork()函数创建进程是如何并发执行的。2)实验内容3是如何实现父子进程的同步执行的。实验项目三 进程调度一、 实验类型本实验为验证性实验。二、 实验目的1.

19、理解进程控制块和进程组织方式;2. 掌握时间片轮转调度算法实现处理机调度。三、 实验预备知识1. 实验基本原理进程控制块通过链表队列的方式组织起来,系统中存在运行队列和就绪队列(为简单起见,不设阻塞队列),进程的调度就是进程控制块在运行队列和就绪队列之间的切换。当需要调度时,从就绪队列中挑选一个进程占用处理机,即从就绪队列中删除一个进程,插入到运行队列中,当占用处理机的进程运行的时间片完成后,放弃处理机,即在运行队列中的进程控制块经过一段时间(时间片)后,从该队列上删除,如果该进程运行完毕,则删除该进程(节点);否则,则插入到就绪队列中。2. 实验中使用的数据结构(1) PCB进程控制块内容包

20、括参数进程名name;要求运行时间 runtime;已运行时间runedtime;本轮运行时间killtime。(2) 进程队列为简单起见,只设运行队列,就绪队列两种数据结构,进程的调度在这两个队列中切换,如图3.1所示。图3.1 PCB链表3. rand()函数和srand()函数库函数中系统提供了两个函数用于产生随机数:srand()和rand()。 函数一:int rand(void);从srand (seed)中指定的seed开始,返回一个0,RAND_MAX(0x7fff)间的随机整数。函数二:void srand(unsigned seed);参数seed是rand()的种子,用来

21、初始化rand()的起始值。函数rand()是真正的随机数生成器,而srand()会设置供rand()使用的随机数种子。如果你在第一次调用rand()之前没有调用srand(),那么系统会为你自动调用srand()。而使用同种子相同的数调用 srand()会导致相同的随机数序列被生成。srand(unsigned)time(NULL)则使用系统定时/计数器的值做为随机种子。每个种子对应一组根据算法预先生成的随机数,所以,在相同的平台环境下,不同时间产生的随机数会是不同的,相应的,若将srand(unsigned)time(NULL)改为srand(TP)(TP为任一常量),则无论何时运行、运行

22、多少次得到的“随机数”都会是一组固定的序列,因此srand生成的随机数是伪随机数。但是,要注意的是所谓的“伪随机数”指的并不是假的随机数。其实绝对的随机数只是一种理想状态的随机数,计算机只能生成相对的随机数即伪随机数。计算机生 成的伪随机数既是随机的又是有规律的一部份遵守一定的规律,一部份则不遵守任何规律。比如“世上没有两片形状完全相同的树叶”,这体现到了事物的特性差异性;但是每种树的叶子都有近似的形状,这正是事物的共性规律性。从这个角度讲,我们就可以接受这样的事实了:计算机只能产生伪随机数而不是绝对的随机数。系统在调用rand()之前都会自动调用srand(),如果用户在rand()之前曾调

23、用过srand()给参数seed指定了一个值,那么 rand()就会将seed的值作为产生伪随机数的初始值;而如果用户在rand()前没有调用过srand(),那么系统默认将1作为伪随机数的初始值。如果给了一个定值,那么每次rand()产生的随机数序列都是一样的。所以为了避免上述情况的发生我们通常用srand( (unsigned) time(0) )或者srand(unsigned)time(NULL)来 产生种子。如果仍然觉得时间间隔太小,可以在(unsigned)time(0)或者(unsigned)time(NULL)后面乘上某个合适的整数。 例如,srand(unsigned)tim

24、e(NULL)*10)。另外,关于time_t time(0):time_t被定义为长整型,它返回从1970年1月1日零时零分零秒到目前为止所经过的时间,单位为秒。srand()、rand()用法举例:#include<time.h>#include<stdio.h>void main() int i,j; srand(10); /srand(int)time(0); for (i=0; i<10; i+) j = (int) (rand()%20; printf(" %dn ", j); 4. malloc()函数头文件:#include&l

25、t;malloc.h>函数原型:void * malloc(unsigned int size);函数说明:其作用是在内存的动态存储区中分配一个长度为size的连续空间,此函数的值(即“返回值”)是一个指向分配域其实地址的指针(类型为void)。若此函数未能成功执行则返回空指针。5. 程序流程图图3.2模拟进程调度的流程图6. 部分参考程序(1) PCB数据结构struct PCB int name; int runtime;int runedtime; int killtime;struct PCB *next;typedef struct PCB PCB;(2) 创建就绪列表#def

26、ine LEN sizeof(PCB)PCB *runqueue;/运行队列指针PCB *top,*tail,*temp;/就绪队列指针int i;srand(int)time(0);for(i=0;i<NUM;i+) temp=(PCB*)malloc(LEN); temp->name=i; temp->runtime=rand()%15; temp->runedtime=0; temp->next=NULL; temp->killtime=0; if(i=0) top=temp; tail=temp; else tail->next=temp; t

27、ail=temp; printf("process name %d, runtime=%d, runedtime=%d,killtime=%dn", tail->name,tail->runtime,tail->runedtime,tail->killtime); 7. 按时间片轮转算法进行进程调度的过程描述。第1步:取就绪队列的队首结点为运行队列的结点,修改就绪队列队首指针后移;第2步:调度运行队列结点,即运行队列结点的要求运行时间减去时间片时间;第3步:a若修改后要求运行时间<=0,则表示该进程结点运行完毕,修改进程结点的PCB信息,记录r

28、untime,runedtime,killtime等信息。并将结点信息输出。b否则,表示该进程结点未完成,记录runtime,runedtime,killtime等信息,将结点信息输出。并将该结点置于就绪队列的队尾,等待下次调度,同时修改队尾指针。第4步:若就绪队列非空,则继续执行第1步,直至就绪队列为空。四、 实验内容1. 建立合理的PCB数据结构,建立含有8个进程结点的就绪队列,每个进程的要求运行时间随机产生,要求每个进程的要求运行时间不大于15。2. 设置时间片大小(36),使用时间片轮转调度算法实现处理机调度。五、 实验报告要求1. 列出实验内容所要求的程序清单,并以截图形式记录相应运

29、行结果;2. 对实验运行结果进行分析:如果时间片设置值过大或过小,会对进程的调度产生何种影响。实验项目四 进程通信一、 实验类型本实验为综合性实验。二、 实验目的1. 了解什么是消息,熟悉消息传送原理。2. 了解和熟悉共享存储机制。3. 掌握消息的发送与接收的实现方法。三、 实验预备知识任务一 消息的发送和接收1. 实验基本原理消息(message)是一个格式化的可变长的信息单元。消息机制允许由一个进程给其它任意的进程发送一个消息。当一个进程收到多个消息时,可将它们排成一个消息队列。消息使用两种重要的数据结构:一是消息首部,其中记录了一些与消息有关的信息,如消息数据的字节数;二是消息队列头表,

30、其每一表项是作为一个消息队列的消息头,记录了消息队列的有关信息。消息机制的数据结构:struct msgform long mtype; char mtext1024;(1)消息首部记录一些与消息有关的信息,如消息的类型、大小、指向消息数据区的指针、消息队列的链接指针等。(2)消息队列头表其每一项作为一个消息队列的消息头,记录了消息队列的有关信息如指向消息队列中第一个消息和指向最后一个消息的指针、队列中消息的数目、队列中消息数据的总字节数、队列所允许消息数据的最大字节总数,还有最近一次执行发送操作的进程标识符和时间、最近一次执行接收操作的进程标识符和时间等。(3)消息队列的描述符UNIX中,每

31、一个消息队列都有一个称为关键字(key)的名字,是由用户指定的;消息队列有一消息队列描述符,其作用与用户文件描述符一样,也是为了方便用户和系统对消息队列的访问。2. msgget( )函数头文件:#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>函数原型:int msgget(key_t key, int msgflag);函数说明:创建一个消息,获得一个消息的描述符。系统内核将搜索消息队列头表,确定是否有指定名字的消息队列。若无,系统内核将分配一新的消息队列头,并对它进行初始化,然后给用户

32、返回一个消息队列描述符,否则它只是检查消息队列的许可权便返回。系统调用格式:msgqid=msgget(key,flag)。其中:msgqid 是该系统调用返回的描述符,失败则返回-1。msgget()函数的第一个参数是消息队列对象的关键字(key),函数将它与已有的消息队列对象的关键字进行比较来判断消息队列对象是否已经创建。而函数进行的具体操作是由第二个参数msgflg 控制的。它可以取下面的几个值:IPC_CREAT : 如果消息队列对象不存在,则创建之,否则则进行打开操作;IPC_EXCL: 和IPC_CREAT 一起使用(用”|”连接),如果消息对象不存在则创建之,否则产生一个错误并返

33、回。 如果单独使用IPC_CREAT 标志,msgget()函数要么返回一个已经存在的消息队列对象的标识符,要么返回一个新建立的消息队列对象的标识符。如果将IPC_CREAT 和IPC_EXCL标志一起使用,msgget()将返回一个新建的消息对象的标识符,或者返回-1。如果消息队列对象已存在,IPC_EXCL 标志本身并没有太大的意义,但和IPC_CREAT 标志一起使用可以用来保证所得的消息队列对象是新创建的而不是打开的已有的对象。除了以上的两个标志以外,在msgflg 标志中还可以有存取权限控制符。这种控制符的意义和文件系统中的权限控制符是类似的。3. msgsnd( )函数头文件:#i

34、nclude<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>函数原型:int msgsnd(int msqid, const void *msgp, intt msgsz, int msgflg);函数说明:在消息队列上进行收发消息,为了发送消息,调用进程对消息队列进行写入时必须有写权能。成功执行时返回0,失败时返回-1。其中msgqid是返回消息队列的描述符;msgp是指向用户消息缓冲区的一个结构体指针。缓冲区中包括消息类型和消息正文,即 long mtype; /*消息类型*/ char mtext

35、; /*消息的文本*/ msgsz指示由msgp指向的数据结构中字符数组的长度;即消息的长度。这个数组的最大值由MSG-MAX( )系统可调用参数来确定。msgflag参数是控制函数行为的标志,取值可以是:0,表示忽略。若在标志msgflag中未设置IPC_NOWAIT位,则当该消息队列中的字节数超过最大值时,或系统范围的消息数超过某一最大值时,调用msgsnd进程睡眠。若是设置IPC_NOWAIT,则在此情况下,msgsnd立即返回。对于msgsnd( ),系统内核须完成以下工作:(1)对消息队列的描述符和许可权及消息长度等进行检查。若合法才继续执行,否则返回;(2)系统内核为消息分配消息数

36、据区。将用户消息缓冲区中的消息正文,拷贝到消息数据区;(3)分配消息首部,并将它链入消息队列的末尾。在消息首部中须填写消息类型、消息大小和指向消息数据区的指针等数据;(4)修改消息队列头中的数据,如队列中的消息数、字节总数等。最后,唤醒等待消息的进程。4. msgrcv ( )函数头文件:#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>函数原型:int msgrcv(int msqid, void *msgp, int msgsz, long msgtyp, int msgflg);函数说

37、明:msgrcv ( )函数用来接收一消息。从指定的消息队列中接收指定类型的消息。其中,msgqid,msgp,msgsz,msgflg与msgsnd中的对应参数相似,msgtyp是规定要读的消息类型,msgflg规定倘若该队列无消息,系统内核应做的操作,是控制函数行为的标志,取值可以是:0,表示忽略;IPC_NOWAIT,如果消息队列为空,则返回一个ENOMSG,并将控制权交回调用函数的进程。如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的消息为止。如此时设置了IPC_NOWAIT标志,则立即返回,若在flag中设置了MS_NOERROR,且所接收的消息大于size,则

38、系统内核截断所接收的消息。对于msgrcv系统调用,系统内核须完成下述工作:(1)对消息队列的描述符和许可权等进行检查。若合法,就往下执行;否则返回;(2)根据msgtyp的不同分成三种情况处理:msgtyp=0,接收该队列的第一个消息,并将它返回给调用者;msgtyp为正整数,接收类型type的第一个消息;msgtyp为负整数,接收小于等于type绝对值的最低类型的第一个消息。(3)当所返回消息大小等于或小于用户的请求时,系统内核便将消息正文拷贝到用户区,并从消息队列中删除此消息,然后唤醒睡眠的发送进程。但如果消息长度比用户要求的大时,则做出错返回。5. msgctl ( )函数头文件:#i

39、nclude<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>函数原型:int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );函数说明:系统调用msgctl ( )用来控制对消息队列的操作。其中,函数调用成功时返回0,不成功则返回-1。buf是用户缓冲区地址,供用户存放控制参数和查询结果;cmd是规定的命令。命令可分三类:(1)IPC_STAT。查询有关消息队列情况的命令。如查询队列中的消息数目、队列中的最大字节数、最后一个发送消息的进程标识符、发

40、送时间等;(2)IPC_SET。按buf指向的结构中的值,设置和改变有关消息队列属性的命令。如改变消息队列的用户标识符、消息队列的许可权等;(3)IPC_RMID。消除消息队列的标识符。msgqid_ds 结构定义如下:struct msgqid_ds struct ipc_perm msg_perm; /*许可权结构*/ short pad17; /*由系统使用*/ ushort msg_qnum; /*队列上消息数*/ ushort msg_qbytes; /*队列上最大字节数*/ ushort msg_lspid; /*最后发送消息的PID*/ ushort msg_lrpid; /*最

41、后接收消息的PID*/ time_t msg_stime; /*最后发送消息的时间*/ time_t msg_rtime; /*最后接收消息的时间*/ time_t msg_ctime; /*最后更改时间*/ ;struct ipc_perm ushort uid; /*当前用户*/ ushort gid; /*当前进程组*/ ushort cuid; /*创建用户*/ ushort cgid; /*创建进程组*/ ushort mode; /*存取许可权*/ short pid1; long pad2; /*由系统使用*/ 6. 样例程序 /client_ex.c 发送端#include&l

42、t;stdio.h>#include<stdlib.h>#include <sys/types.h>#include <sys/msg.h>#include <sys/ipc.h>#define MSGKEY 75struct msgform long mtype; char mtext1024;msg;int msgqid;void client() msgqid=msgget(MSGKEY,0777); /*打开75#消息队列*/ msg.mtype=1; msg.mtext1='a' printf("(cli

43、ent)sentn"); msgsnd(msgqid,&msg,1024,0); /*发送消息*/ exit(0);int main( ) client( ); /server_ex.c 接收端#include<stdio.h>#include<stdlib.h>#include <sys/types.h>#include <sys/msg.h>#include <sys/ipc.h>#define MSGKEY 75struct msgform long mtype; char mtext1024;msg;int

44、msgqid;void server( ) msgqid=msgget(MSGKEY,0777|IPC_CREAT); /*创建75#消息队列*/ msgrcv(msgqid,&msg,1024,0,0); /*接收消息*/ printf("%c ",msg.mtextmsg.mtype); printf("(server)receivedn"); msgctl(msgqid,IPC_RMID,0); /*删除消息队列,归还资源*/ exit(0);int main( ) server( );任务二 使用共享存储区通信1. 实验基本原理共享存储区

45、(Share Memory)是UNIX系统中通信速度最高的一种通信机制。该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个进程的虚地址空间中。另一方面,一个进程的虚地址空间中又可连接多个共享存储区,每个共享存储区都有自己的名字。当进程间欲利用共享存储区进行通信时,必须先在主存中建立一共享存储区,然后将它附接到自己的虚地址空间上。此后,进程对该区的访问操作,与对其虚地址空间的其它部分的操作完全相同。进程之间便可通过对共享存储区中数据的读、写来进行直接通信。图4.1列出二个进程通过共享一个共享存储区来进行通信的例子。其中,进程A将建立的共享存储区附接到自己的AA'区域,

46、进程B将它附接到自己的BB'区域。图4.1 共享存储区应当指出,共享存储区机制只为进程提供了用于实现通信的共享存储区和对共享存储区进行操作的手段,然而并未提供对该区进行互斥访问及进程同步的措施。因而当用户需要使用该机制时,必须自己设置同步和互斥措施才能保证实现正确的通信。2. shmget ( )函数头文件:#include<sys/types.h>#include<sys/ipc.h>#include<sys/shm.h>函数原型:int shmget(key_t key, size_t size, int shmflg);函数说明:得到一个共享内

47、存标识符或创建一个共享内存对象并返回共享内存标识符。其中,key是共享存储区的名字;size是其大小(以字节计); shmflg是用户设置的标志,如IPC_CREAT。IPC_CREAT表示若系统中尚无指名的共享存储区,则由系统内核建立一个共享存储区;若系统中已有共享存储区,便忽略IPC_CREAT,并返回此共享内存的标识符。在shmflg 标志中还可以有存取权限控制符。这种控制符的意义和文件系统中的权限控制符是类似的。3. shmat ( )函数头文件:#include<sys/types.h>#include<sys/ipc.h>#include<sys/sh

48、m.h>函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg);函数说明:把共享内存区对象映射到调用进程的地址空间,系统调用的返回值是共享存储区所附接到的进程虚地址。连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。其中,shmid是共享存储区的标识符;shmaddr是用户给定的,将共享存储区附接到进程的虚地址空间;shmflg规定共享存储区的读、写权限,以及系统是否应对用户规定的地址做舍入操作。其值为SHM_RDONLY时,表示只能读;其值为0时,表示

49、可读、可写;其值为SHM_RND(取整)时,表示操作系统在必要时舍去这个地址。4. shmdt ( )函数头文件:#include<sys/types.h>#include<sys/ipc.h>#include<sys/shm.h>函数原型:int shmdt(const void *shmaddr);函数说明:与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此共享内存。其中,shmaddr是要断开连接的虚地址,亦即以前由连接的系统调用shmat( )所返回的虚地址。调用成功时,返回0值,调用不成功,返回-1。本函数调用并不删除所指定的

50、共享内存区,而只是将先前用shmat函数连接(attach)好的共享内存脱离(detach)目前的进程。5. shctl ( )函数头文件:#include<sys/types.h>#include<sys/ipc.h>#include<sys/shm.h>函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);函数说明:系统调用shmctl ( )用来控制对共享存储区的操作,对其状态信息进行读取和修改。其中,shmid是共享存储区的标识符,buf是用户缓冲区地址,cmd是操作命令。命令可分为多种类

51、型:(1)用于查询有关共享存储区的情况。如其长度、当前连接的进程数、共享区的创建者标识符等;(2)用于设置或改变共享存储区的属性。如共享存储区的许可权、当前连接的进程计数等;(3)对共享存储区的加锁和解锁命令;(4)删除共享存储区标识符等。上述的查询是将shmid所指示的数据结构中的有关成员,放入所指示的缓冲区中;而设置是用由buf所指示的缓冲区内容来设置由shmid所指示的数据结构中的相应成员。cmd有下列几种数值:IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中;IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、m

52、ode复制到共享内存的shmid_ds结构内;IPC_RMID:删除这片共享内存6. 样例程序 /client_ex.c 发送端#include <sys/types.h>#include <sys/shm.h>#include <sys/ipc.h>#include<stdio.h>#include<stdlib.h>#define SHMKEY 75int shmid,i;int *addr;void client( ) int i; shmid=shmget(SHMKEY,1024,0777); /*打开共享存储区*/ addr

53、=shmat(shmid,0,0); /*获得共享存储区首地址*/ while (*addr!=-1); printf("(client) sentn"); *addr=100; exit(0);int main( ) client( ); /server_ex.c 接收端#include <sys/types.h>#include <sys/shm.h>#include <sys/ipc.h>#include<stdio.h>#include<stdlib.h>#define SHMKEY 75int shmid

54、,i;int *addr;void server( ) shmid=shmget(SHMKEY,1024,0777|IPC_CREAT); /*创建共享存储区*/ addr=shmat(shmid,0,0); /*获取首地址*/ *addr=-1; while (*addr=-1); printf("%d",*addr); printf(" (server) receivedn"); shmctl(shmid,IPC_RMID,0); /*撤消共享存储区,归还资源*/ exit(0);int main( ) server( );四、 实验内容1. 根据消

55、息传送机理,使用系统调用msgget( ), msgsnd( ), msgrev( ), 及msgctl( )编制一长度为k的消息发送和接收的程序,要求在程序中完成10次消息的发送和接收,每次发送消息结束和接收消息结束都需给出相应的屏幕提示,且每次发送的的内容不少于一个字符,并能在接收端输出。2. 根据共享存储区原理,使用系统调用shmget( ), shmat( ), shmdt( ), 及shctl( )编制程序,要求创建一个长度为k的共享存储区,并完成10次数据的发送和接收,每次发送数据结束和接收数据结束都需给出相应的屏幕提示,且每次发送的的数据应能在接收端输出。五、 实验报告要求1. 列出实验内容所要求的程序清单,并以截图形式记录相应运行结果。2. 对实验运行结果进行分析: 试比较实验中两种方法实现进程通信的不同之处。实验项目五 存储管理一、 实验类型本实验为验证性实验。二、 实验目的1. 熟悉内存空闲分区的分配方式;2. 理解动态分区存储管理方式;3. 掌握动态分区的分配与回收的过程。三、 实验预备知识1. 实验基本原理使用一个链表来模拟内存存储空间,建立内存块来记录内存分配使用情况,通过随机产生进程及其所需要的内存来模拟真实的进程

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论