《操作系统》实验报告
实验序号: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小时内删除。
发表评论