深圳大学SZU操作系统实验1:并发程序设计

一、实验目的与要求

通过进程的创建、撤销和运行加深对进程概念和进程并发执行的理解,明确进程与程序之间的区别。

二、方法、步骤:

  1. 掌握在Linux中编译运行的方法。
  2. 阅读例程,理解函数fork()、execl()、exit()、getpid()和waitpid()的功能和用法
  3. 运行例程,分析例程中关键代码的功能,给出运行结果并对运行结果进行分析说明。
  4. 模仿例程,编写一段程序实现以下功能:
    1. 使用系统调用fork()创建两个子进程
    2. 各个子进程显示和输出一些提示信息和自己的进程标识符。
    3. 父进程显示自己的进程ID和一些提示信息,然后调用waitpid()等待多个子进程结束,并在子进程结束后显示输出提示信息表示程序结束。
  5. 创建多个(3个以上)进程并发运行,控制好各个子进程输出自己的进程标识符和一些提示信息,对程序运行结果进行分析说明。观察各个子进程并发执行的顺序,输出结果是否与设想中的顺序不同,并分析原因。
  6. 实现完美二叉树形式的进程树

三.实验过程及内容:

1.掌握在Linux中编译运行的方法

  1. 编写源代码:使用文本编辑器或者IDE编写C或C++源代码文件,例如hello.c或hello.cpp
  2. 保存源代码:将源代码文件保存在工作目录
  3. 打开终端:打开Linux终端
  4. 切换到源代码所在目录:使用cd命令,例如cd /path/codes
  5. 编译源代码:使用GCC编译器编译源代码

         例如:gcc -o output_filename input_filename.c

运行可执行文件:编译成功后,可以运行生成的可执行文件。

例如:./output_filename

终端中将显示程序的输出。

2. 阅读例程,理解函数 fork()、execl()、exit()、getpid()和 waitpid()的功能和用法

2.1 fork 函数

功能:fork()创建一个新的进程,新进程是调用进程的副本。

 

在父进程中,fork()返回新创建子进程的进程 ID;在子进程中,fork()返回 0。如果出现错误,fork()返回-1。

用法:

图1.fork函数用法示例

2.2 execl函数

功能:用于在当前进程中执行另一个程序,通常与fork()结合使用。execl()的参数包括要执行的程序的路径以及传递给该程序的参数,最后一个参数必须是NULL。

用法:execl("/bin/ls", "ls", "-l", NULL);

2.3 exit函数

功能:用于终止当前进程的执行,并返回一个状态码。这个状态码可以被父进程通过wait()或waitpid()函数获取。

用法:exit(0); // 正常退出

exit(1); // 异常退出

2.4 getpid函数

功能:获取当前进程的进程ID。

用法:pid_t pid = getpid();

2.5 waitpid函数

功能:等待特定子进程的结束,阻塞父进程直到子进程结束或出错。它可以指定要等待的子进程ID,也可以使用-1表示等待任意子进程。

用法:int status;

pid_t child_pid = waitpid(-1, &status, 0);

3.运行例程,分析例程中关键代码的功能,给出运行结果并对运行结果进行分析说明。

3.1 例程1

代码分析:简单的输出函数,终端显示Hello World!!

运行结果:

图2. 例程1运行结果

结果分析:正常输出,说明编译运行环境正常

3.2 例程2

代码分析:

(1)自定义tpirntf函数输出hh:mm:ss格式的当前时间和当前进程pid

(2) 父进程首先输出自己的进程ID(PID),然后调用fork()创建子进程。

子进程在创建后会输出自己的进程ID,并且循环3次输出自己的ID和循环次数,每次间隔1秒。

父进程在创建子进程后会输出一条消息表示fork了一个子进程,并等待子进程退出。

父进程调用waitpid()等待子进程退出,然后输出子进程退出的消息和自己退出的消息。

运行结果:

图3. 例程2运行结果

结果分析:父进程创建一个子进程,父子进程各自输出一些信息

3.3 例程3

代码分析:

父进程调用fork()创建子进程,子进程在创建后等待5秒(使用sleep(5)),然后输出一条信息表示子进程正在运行,并调用execl("/bin/ps", "-a", NULL);来执行ps -a命令查看进程列表。

父进程在创建子进程后输出一条信息表示父进程正在运行,并等待1秒。随后输出一条信息表示fork了一个子进程,并调用waitpid()等待子进程退出。

子进程执行execl()后,会替换自己的进程映像为ps -a命令,因此之后的代码不会被执行。

运行结果:

图3. 例程3运行结果

结果分析:execl函数是用于执行指定路径下的可执行文件的一个函数。它的作用是将当前进程替换为一个新的进程,并执行指定的可执行文件。在替换之后,原进程的代码、数据和堆栈等都会被新进程完全取代,原进程的执行流程也就结束了。新进程继承了原进程的PID,因此在正常情况下,execl函数调用后的代码是不会再执行的,除非调用失败才会继续执行后续的代码。

You should never see this because the child is already gone.这条消息并没有出现,因为子进程已经被ps -a替换了。

4、  模仿例程,编写一段程序实现以下功能:

a)    使用系统调用fork()创建两个子进程

b)    各个子进程显示和输出一些提示信息和自己的进程标识符。

c)    父进程显示自己的进程ID和一些提示信息,然后调用waitpid()等待多个子进程结束,并在子进程结束后显示输出提示信息表示程序结束。

图4. 生成2个子进程的代码

运行结果:

图5. 生成2个子进程的结果

5、  创建多个(3个以上)进程并发运行,控制好各个子进程输出自己的进程标识符和一些提示信息,对程序运行结果进行分析说明。观察各个子进程并发执行的顺序,输出结果是否与设想中的顺序不同,并分析原因。

图6. 创建4个进程的代码

代码思路:使用信号量,控制每次最多只有1个子进程在运行,实现并发控制

设想:输出顺序为子进程1,2,3,4,父进程

实际结果:子进程执行顺序并不总是按照1,2,3,4

图7. 创建4个进程的运行结果

分析:这是由于操作系统调度算法的不确定性所导致的。操作系统可能会根据进程的优先级、运行时间、等待时间等因素来决定进程的执行顺序。因此,每个子进程执行的顺序并不是确定的。

6.  完美二叉树形式的进程树

思路:每个父进程内2次调用fork()创建子进程,然后在每个进程中根据当前的层数计算出当前进程的编号(numb),输出进程编号,pid, ppid

图8.完美二叉树进程树代码

运行结果:以3层的二叉树为例。不使用pstree,是因为父进程使用waitpid来等待每个子进程的结束,找不到使用pstree合适的位置。

图9. 完美二叉树进程树运行结果

图10. 二叉树中各个进程的PID

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值