操作系统进程实验

华中农业大学 学生实验报告

日期: 2019 年 11 月 28 日 成绩
课程名称 计算机操作系统 实验名称 进程控制与通信 实验类型 验证 设计
综合 创新
【实验目的】
实验目的:1、掌握进程的概念,明确进程和程序的区别。
2、认识和了解并发执行的实质。
3、分析进程争用资源的现象,学习解决进程互斥的方法。

实验要求:linux 环境下完成实验
【实验内容】
(一) 进程控制
(二) 进程间通信
(1) 信号量机制实验
(2) 进程的管道通信实验
(3) 消息的发送与接收实验
(4) 共享存储区通信
【实验环境】(含主要设计设备、器材、软件等)
Pc电脑一台

【实验步骤、过程】(含原理图、流程图、关键代码,或实验过程中的记录、数据等)
内容:
(一) 进程控制
1、进程的创建(必做题) 编写一段程序,使用系统调用 fork( )创建两个子进程,在系统中有一个父进程和两个子进程 活动。让每个进程在屏幕上显示一个字符;父进程显示字符“a”,子进程分别显示字符“b” 和“c”。 试观察记录屏幕上的显示结果,并分析原因。

原因是:在执行到fork()之前,只有一个进程在运行,执行到fork()后,父进程产生子进程,父进程的返回值大于0,子进程的返回值等于0,用于区分父子进程;之后父子进程并行执行并没有先后顺序之分(取决操作系统),所以使得运行结果不同。
2、 修改已编写的程序,将每个进程的输出由单个字符改为一句话,再观察程序执行时屏幕上出现 的现象,并分析其原因。(必做题)

原因跟上一道题目类似,父进程产生两个并行的子进程,两个子进程执行的顺序不能确定。
3、编写程序创建进程树如图 1 和图 2 所示,在每个进程中显示当前进程识别码和父进程识别码。(必 做题)

【思考题】
1、 系统是怎样创建进程的?
申请空白PCB
为新进程分配资源
初始化进程控制块(初始化标识信息,初始化处理机状态信息,初始化处理机控制信息)
将新进程插入就绪队列
2、 当首次调用新创建进程时,其入口在哪里?
进程的进程控制块(PCB)结构中的指向其TTS(任务状态段)的指针。
3、 当前运行的程序(主进程)的父进程是什么?
当前运行程序的父进程是应该是该进程的上一个进程
(二) 进程间通信
(1) 信号量机制实验
1.编写一段程序,使用系统调用 fork( )创建两个子进程,再用系统调用 signal( )让父进程捕捉 键盘上来的中断信号(即按 ctrl+c 键),当捕捉到中断信号后,父进程用系统调用 kill( )向两个 子进程发出信号,子进程捕捉到信号后,分别输出下列信息后终止:
Child process 1 is killed by parent!
Child process 2 is killed by parent!
父进程等待两个子进程终止后,输出以下信息后终止:
Parent process is killed!

结果:

实验要求:
(1)、运行程序并分析结果。
结果分析:在父进程产生两个进程之前,就对父进程进行signal设置,因为signal函数的意义就是如果产生了SIGINT信号,就会执行stop指令。于是结束了waiting()等待的指令产生了中断处理,而子进程同样也会相应产生中断。
(2)、如果把 signal(SIGINT,stop)放在①号和②号位置,结果会怎样并分析原因。
结果分析:根据signal的位置不同,发现结果完全不同,究其原因还是fork()函数会使得子进程集成父进程的所有信息包括软中断的信息,所以使得不同的位置控制不同的子进程。
(3)、该程序段前面部分用了两个 wait(0),为什么?
等待子进程运行结束。如果子进程没有完成,父进程一直等待。wait( )将调用进程挂起,直至 其子进程因暂停或终止而发来软中断信号为止。如果在 wait( )前已有子进程暂停或终止,则调用 进程做适当处理后便返回。
(4)、该程序段中每个进程退出时都用了语句 exit(0),为什么?
终止进程的执行。
2、修改上面的程序,增加语句 signal(SIGINT,SIG_IGN)和语句 signal(SIGQUIT,SIG_IGN),再观察 程序执行时屏幕上出现的现象,并分析其原因。

大体上的理解与上一题类似,但是注意后一句的signal 对之前设置的有覆盖作用。

3.编程用 fork()创建一个子进程代表售票员,司机在父进程中,再用系统调用 signal()让父进程 (司机)捕捉来自子进程(售票员)发出的中断信号,让子进程(售票员)捕捉来自(司机)发出 的中断信号,以实现进程间的同步运行

(2) 进程的管道通信实验
编制一段程序,实现进程的管道通信。使用 pipe()建立一条管道线。两个子进程 p1 和 p2 分别 向管道各写一句话:
Child 1 is sending message! Child 2 is sending message!
而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。

分析结果:所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信 的一个共享文件,又称为 pipe 文件。由写进程从管道的写入端(句柄 1)将数据写入管道,而读进 程则从管道的读出端(句柄 0)读出数据。所以两个子进程往fd[1]中写入内容,父进程从fd[0]中读取,所以才会有如上结果。
2.在父进程中用 pipe()建立一条管道线,往管道里写一句话,两个子进程接收这句话。

(3) 消息的发送与接收实验
1、 消息的创建、发送和接收。使用系统调用 msgget( ),msgsnd( ),msgrev( ),及 msgctl( )编制一 长度为1k 的消息发送和接收的程序。

程序说明:
① 为了便于操作和观察结果,编制二个程序 client.c 和 server.c,分别用于消息的发送与接收。
② server 建立一个 Key 为 75 的消息队列,等待其它进程发来的消息。当遇到类型为 1 的消息,则 作为 结束信号,取 消该队列 ,并退 出 server。server 每接收 到一个消 息后显示一句 “(server)received。”
③client 使用 key 为 75 的消息队列,先后发送类型从 10 到 1 的消息,然后退出。最后一个消息,即是 server 端需要的结束信号。
2、 在父进程中创建一个消息队列,用 fork 创建一个子进程,在子进程中将一条消息传送至消息队 列,父进程接受队列的消息,并将消息送屏幕显示。

  1. msgget( )
    创建一个消息,获得一个消息的描述符。核心将搜索消息队列头表,确定是否有指定名字的消 息队列。若无,核心将分配一新的消息队列头,并对它进行初始化,然后给用户返回一个消息队列 描述符,否则它只是检查消息队列的许可权便返回。 系统调用格式:
    msgqid=msgget(key,flag)
    该函数使用头文件如下:
    #include
    #include
    #include 参数定义 int msgget(key,flag)
    key_t key; int flag; 其中:key 是用户指定的消息队列的名字;flag 是用户设置的标志和访问方式。
    如 IPC_CREAT |0400 是否该队列已被创建。无则创建,是则打开;
    IPC_EXCL |0400 是否该队列的创建应是互斥的。
    msgqid 是该系统调用返回的描述符,失败则返回-1。
  2. msgsnd()
    发送一消息。向指定的消息队列发送一个消息,并将该消息链接到该消息队列的尾部。
    系统调用格式: msgsnd(msgqid,msgp,size,flag) 该函数使用头文件如下: #include
    #include
    #include
    参数定义: int msgsnd(msgqid,msgp,size,flag)
    int msgqid,size,flag;
    struct msgbuf * msgp;
    其中 msgqid 是返回消息队列的描述符
    ;msgp 是指向用户消息缓冲区的一个结构体指针。缓冲 区中包括消息类型和消息正文,即
    {
    long mtype; /消息类型/
    char mtext[ ]; /消息的文本/
    } size 指示由 msgp 指向的数据结构中字符数组的长度;即消息的长度。这个数组的最大值由 MSG-MAX( )系统可调用参数来确定。
    flag 规定当核心用尽内部缓冲空间时应执行的动作:进程是等 待,还是立即返回。若在标志 flag 中未设置 IPC_NOWAIT 位,则当该消息队列中的字节数超过最大 值时,或系统范围的消息数超过某一最大值时,调用 msgsnd 进程睡眠。若是设置 IPC_NOWAIT,则 在此情况下,msgsnd 立即返回。
    对于 msgsnd( ),核心须完成以下工作:
    (1)对消息队列的描述符和许可权及消息长度等进行检查。若合法才继续执行,否则返回;
    (2)核心为消息分配消息数据区。将用户消息缓冲区中的消息正文,拷贝到消息数据区;
    (3)分配消息首部,并将它链入消息队列的末尾。在消息首部中须填写消息类型、消息大小和指向 消息数据区的指针等数据; (4)修改消息队列头中的数据,如队列中的消息数、字节总数等。最后,唤醒等待消息的进程。
  3. msgrcv( )
    接受一消息。从指定的消息队列中接收指定类型的消息。 系统调用格式: msgrcv(msgqid,msgp,size,type,flag) 本函数使用的头文件如下:
    #include
    #include
    #include 参数定义:
    int msgrcv(msgqid,msgp,size,type,flag)
    int msgqid,size,flag;
    struct msgbuf *msgp;
    long type;
    其中,msgqid,msgp,size,flag 与 msgsnd 中的对应参数相似,type 是规定要读的消息类型,flag 规定倘若该队列无消息,核心应做的操作。如此时设置了 IPC_NOWAIT 标志,则立即返回,若在 flag 中设置了 MS_NOERROR,且所接收的消息大于 size,则核心截断所接收的消息。
    对于 msgrcv 系统调用,核心须完成下述工作:
    (1)对消息队列的描述符和许可权等进行检查。若合法,就往下执行;否则返回;
    (2)根据 type 的不同分成三种情况处理:
    type=0,接收该队列的第一个消息,并将它返回给调用者;
    type 为正整数,接收类型 type 的第一个消息;
    type 为负整数,接收小于等于 type 绝对值的最低类型的第一个消息。
    (3)当所返回消息大小等于或小于用户的请求时,核心便将消息正文拷贝到用户区,并从消息队列 中删除此消息,然后唤醒睡眠的发送进程。但如果消息长度比用户要求的大时,则做出错返回。
  4. msgctl( )
    消息队列的操纵。读取消息队列的状态信息并进行修改,如查询消息队列描述符、修改它的许 可权及删除该队列等。 系统调用格式: msgctl(msgqid,cmd,buf);
    本函数使用的头文件如下:
    #include
    #include
    #include
    参数定义: int msgctl(msgqid,cmd,buf);
    int msgqid,cmd;
    struct msgqid_ds *buf;
    其中,函数调用成功时返回 0,不成功则返回-1。buf 是用户缓冲区地址,供用户存放控制参数 和查询结果;cmd 是规定的命令。命令可分三类:
    (1)IPC_STAT。查询有关消息队列情况的命令。如查询队列中的消息数目、队列中的最大字节数、 最后一个发送消息的进程标识符、发送时间等;
    (2)IPC_SET。按 buf 指向的结构中的值,设置和改变有关消息队列属性的命令。如改变消息队列 的用户标识符、消息队列的许可权等;
    (3)IPC_RMID。消除消息队列的标识符。
    (4) 共享存储区通信
    1、编制一长度为 1k 的共享存储区发送和接收的程序。
    <程序说明>
    1.为了便于操作和观察结果,用一个程序作为“引子“,先后 fork()两个子进程,server 和 client, 进行通信。
    2.server 端建立一个 key 为 75 的共享区,并将第一个字节置为-1,作为数据空的标志。等待其他 进程发来的消息。当该字节的值发生变化时,表示收到了信息,进行处理。然后再次把它的值设为 -1,如果遇到的值为 0,则视为为结束信号,取消该队列,并退出 server。server 每接收到一次数 据后显示“(server)received”。
    3.client 端建立一个 key 为 75 的共享区,当共享取得第一个字节为-1 时,server 端空闲,可发送 请求。client 随即填入 9 到 0。期间等待 server 端的再次空闲。进行完这些操作后,client 退出。 client 每发送一次数据后显示“(client)sent”。
    4.父进程在 server 和 client 均退出后结束。

对共享存储的理解首先要知道需要先在共享存储区内申请一块区域,用来接收要写的信息。
之后建立链接,把共享存储区的内容映射到虚空间内。shmget( ) 创建、获得一个共享存储区。shmat( ) 共享存储区的附接。从逻辑上将一个共享存储区附接到进程的虚拟地址空间上。shmdt( ) 把一个共享存储区从指定进程的虚地址空间断开。
3、 编程在主进程中创建两个子进程,在子进程 shmw()中创建一个系统 V 共享内存区,并在其中写 入格式化数据;在子进程 shmr()中访问同一个系统 V 共享内存区,读出其中的格式化数据。

【实验结果或总结】(对实验结果进行相应分析,或总结实验的心得体会,并提出实验的改进意见)
进程控制 总结:

  1. 更深刻的理解了fork()函数以及父子进程的执行的无序性。
  2. 对linux环境有了更深刻的认识。
    软中断 总结:
    1.更深刻的懂得fork()的继承性。
    2.对软中断的各个函数有了了解。
    3.进一步了解信号与中断的联系与区别。
    管道通信 总结:
    1.了解了pipe 文件建立
    2.注意管道内容只能输送一次
    消息传送机理 总结:
    1.重点掌握消息传递的原理
    2.掌握消息传递的思想
    3.掌握消息传递的各个函数
    4.消息传递机制较为重要
    共享存储机制 总结:
    1.懂得了共享存储机制的原理
    2.了解了共享存储机制的函数
    通过这次实验对理论课上所讲的fork()函数以及软中断等方法有了更深刻的认识。

你可能感兴趣的:(操作系统)