《操作系统》实验报告
实验序号:3                            实验项目名称:进程管理
学  号
姓  名
ZRZ
专业、班
18计卓1班
实验地点
指导教师
李远敏
实验时间
2020.4.10
一、实验目的及要求
1. 加深对进程概念的理解,明确进程和程序的区别。
2. 进一步认识并发执行的实质。
3. 分析进程争用资源的现象,学习解决进程互斥的方法
二、实验设备(环境)及要求
1.Ubuntu操作系统环境
2.搭建Ubuntu操作系统下的C语言编程环境。
三、实验内容与步骤
1.进程的创建一:
(1)编写一段程序,使用系统调用fork()创建两个子进程。 
(2)当此程序运行时,在系统中有一个父进程和两个子进程活动。
(3)让每一个进程在屏幕上显示一个字符:父进程显示字符“a”;子进程分别显示字符“b”和字符“c”。 
(4)试观察记录屏幕上的显示结果,并分析原因。
2.进程的创建二:
(1)使用vi将程序清单2-4输入
(2)将fork函数换成vfork函数程序有什么变化,指出fork函数与vfork函数的区别
(3)利用gcc编译这个程序,写出编译命令和执行结果;如果不成功,尝试利用gdb调试
(4)仔细阅读程序,并画出程序流程图。
3.进程的控制:
(1)修改实验内容1编写的程序,将每个进程输出一个字符改为每个进程输出一句话。
(2)观察程序执行时屏幕上出现的现象,并分析原因。
(3)如果在程序中使用系统调用lockf()来给每一个进程加锁,可以实现进程之间的互斥,观察并分析出现的现象。
4.进程创建替换控制:
(1)用fork()创建一个进程,再调用exec()用新的程序替换该子进程的内容
(2)利用wait()来控制进程执行顺序。
linux下的sleep函数
四、实验结果与数据处理
1.进程的创建一
(1)实验结果:
1:进程的创建一代码
2:程序运行结果
(2)结果分析:
运行结果:我们由图可以得知输出结果为bca或者abc的任意排列。
结果分析:我们从进程执行并发的角度来思考,不管输出abc的任意排列都是有可能的。
原因:fork函数的原理是:一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
而fork()创建进程所需的时间虽然可能多于输出一个字符的时间,但各个进程的时间片的获得却不是一定是顺序的,所以输出abc的排列都是有可能的。
(3)程序修改:
1.如果想让子进程先执行,父进程后执行,利用wait()/waitpid()函数怎样修改程序实现。
2.如果让多个进程不并发执行,而是按规定的顺序执行,请利用sleep()函数怎样修改程序实现。
3:修改后的代码
4:修改后的代码运行结果
从代码修改后的运行结果和网上查阅的资料我们可以知道wait()/waitpid()和sleep()函数都可以控制进程执行的先后顺序,而我们也可以深入了解一下wait()和waitpid()的区别:
a. wait()函数用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或者该进程接收到了一个指定的信号为止。如果该父进程没有子进程或者它的子进程已经结束,则wait()函数就会立即返回。
b. waitpid()的作用和wait()一样,但它并不一定要等待第一个终止的子进程(它可以指定需要等待终止的子进程),它还有若干选项,如可提供一个非阻塞版本的 wait()功能,也能支持作业控制。实际上,wait()函数只是 waitpid()函数的一个特例,在Linux 内部实现 wait()函数时直接调用的就是waitpid()函数。
2.进程的创建二
(1)实验结果:
  输入gcc -Wall ex_forc.c -o ex_forc 
   
5:fork()函数程序运行结果
输入gcc -Wall ex_forc.c -o ex_forc
        ./ex_forc
6:vfork()函数程序运行结果
(2)结果分析:
使用fork()函数由于子进程与父进程的运行是无关的,父进程可先于子进程运行,子进程也可先于父进程运行,而使用vfork()函数则一定是子进程先于父进程运行。他们的区别是:
a.fork()用于创建一个新进程。由fork()创建的子进程是父进程的副本。即子进程获取父进程数据空间,堆和栈的副本。父子进程之间不共享这些存储空间的部分。而vfork()创建的进程并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec (或exit)于是也就不会存放该地址空间。相反,在子进程调用exec或exit之前,它在父进程的空间进行。
  b.vfork()与fork()另一个区别就是:vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。
7:fork()函数程序
图8:vfork()函数程序
8:vfork()函数程序
9:程序流程图
3.进程的控制
(1)实验结果:
  输入gcc -Wall jckz.c -o jckz
10:无lockf()程序运行结果
结果分析:由于函数printf()输出的字符串之间不会被中断,因此,每个字符串内部的字符顺序输出时不变。但是,由于进程并发执行时的调度顺序和父子进程的抢占处理机问题,输出字符串的顺序和先后随着执行的不同而发生变化。这与打印单字符的结果相同。
11:有lockf()程序运行结果
12:无lockf()程序代码
13:有lockf()程序代码
(2)结果分析:
运行结果:我们可以从图中得知,上锁的输出结果大致与未上锁的输出结果相同,也是随着执行时间不同,输出结果的顺序会有所不同。
结果分析:因为上述程序执行时,不同进程之间不存在共享临界资源(其中打印机的互斥性已有由操作系统保证)问题,所以,加锁与不加锁效果相同。
(3)总结:
因为第一次使用lockf(),刚开始还不明白lockf()中的参数到底应该怎样用,后来在网上查询了资料,才明白了lockf(int fd, int cmd, off_t len)中的参数分别为:fd 是打开文件的文件描述符,cmd 是指定要采取的操作的控制值,len是要锁定或解锁的连续字节数。
lockf()函数允许将文件区域用作信号量(监视锁),或用于控制对锁定进程的访问(强制模式记录锁定)。试图访问已锁定资源的其他进程将返回错误或进入休眠状态,直到资源解除锁定为止。当关闭文件时,将释放进程的所有锁定,即使进程仍然有打开的文件。当进程终止时,将释放进程保留的所有锁定。
此函数调用成功后,将返回值 0,否则返回−1,并且设置 errno 以表示该错误。
4.进程创建替换控制
(1)实验结果:
14:程序运行结果1
15:程序运行结果2
16:程序代码
执行命令ls -l ,列出当前目录下所有文件和子目录;
ls completed!
(2)结果分析:
程序在调用fork()建立一个子进程后,马上调用wait(),使父进程在子进程结束之前,一直处于睡眠状态。子进程用exec()装入命令ls,exec()后,子进程的代码被ls的代码取代,这时子进程的PC指向ls的第1条语句,开始执行ls的命令代码。
(3)思考
a.可执行文件加载时进行了哪些处理?
可执行文件加载时首先是创建一个新进程的fork系统调用,然后用于实现进程自我终止的exit系统调用;改变进程原有代码的exec系统调用;用于将调用进程挂起并等待子进程终止的wait系统调用;获得进程标识符的getpid系统调用等处理过程。
b.什么是进程同步?wait()是如何实现进程同步的?
进程同步是指对多个相关进程在执行次序上进行协调,以使并发执行的主进程之间有效地共享资源和相互合作,从而使程序的执行具有可在现行。 
首先程序在调用fork()机那里了一个子进程后,马上调用wait(),使父进程在子进程调用之前一直处于睡眠状态,这样就使子进程先运行,子进程运行exec()装入命令后,然后调用wait(0),使子进程和父进程并发执行,实现进程同步。
5、分析与讨论
通过这一次的实验,我对进程的控制和管理有了更多的了解,也学会了如何对子进程和父进程进行相应的操作,明白了fork()、vfork()、exec()、wait()、waitpid()、sleep()、lockf()等等函数,对于进程的整个运行过程和运行方式有了更加深入地体会,让我对linux操作系统的学习更加得心应手了。
六、教师评语
签名:
日期:
成绩

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。