实验三-进程管理
操作系统实验报告进程管理

操作系统实验报告进程管理操作系统实验报告:进程管理引言操作系统是计算机系统中的核心软件,负责管理计算机的硬件资源和提供用户与计算机之间的接口。
进程管理是操作系统的重要功能之一,它负责对计算机中运行的各个进程进行管理和调度,以保证系统的高效运行。
本实验报告将介绍进程管理的基本概念、原理和实验结果。
一、进程管理的基本概念1. 进程与线程进程是计算机中正在运行的程序的实例,它拥有独立的内存空间和执行环境。
线程是进程中的一个执行单元,多个线程可以共享同一个进程的资源。
进程和线程是操作系统中最基本的执行单位。
2. 进程状态进程在运行过程中会经历不同的状态,常见的进程状态包括就绪、运行和阻塞。
就绪状态表示进程已经准备好执行,但还没有得到处理器的分配;运行状态表示进程正在执行;阻塞状态表示进程由于某些原因无法继续执行,需要等待某些事件的发生。
3. 进程调度进程调度是操作系统中的一个重要任务,它决定了哪个进程应该获得处理器的使用权。
常见的调度算法包括先来先服务(FCFS)、最短作业优先(SJF)和时间片轮转等。
二、进程管理的原理1. 进程控制块(PCB)PCB是操作系统中用于管理进程的数据结构,它包含了进程的各种属性和状态信息,如进程标识符、程序计数器、寄存器值等。
通过PCB,操作系统可以对进程进行管理和控制。
2. 进程创建与撤销进程的创建是指操作系统根据用户的请求创建一个新的进程。
进程的撤销是指操作系统根据某种条件或用户的请求终止一个正在运行的进程。
进程的创建和撤销是操作系统中的基本操作之一。
3. 进程同步与通信多个进程之间可能需要进行同步和通信,以实现数据共享和协作。
常见的进程同步与通信机制包括互斥锁、信号量和管道等。
三、实验结果与分析在本次实验中,我们使用了一个简单的进程管理模拟程序,模拟了进程的创建、撤销和调度过程。
通过该程序,我们可以观察到不同调度算法对系统性能的影响。
实验结果显示,先来先服务(FCFS)调度算法在一些情况下可能导致长作业等待时间过长,影响系统的响应速度。
操作系统实验,实验3, 进程管理 (1)

在图形界面上利用终端通过命令“su - ”切换到超级用户,编辑文件 “job”; 按组合键[Ctrl+Alt+F2]切换到第二个虚拟终端(超级用户); 输入命令“at –f job now+1 minute”,设置1分钟后执行at调度内容; 稍等后观察at调度的执行效果,再切换到第一个虚拟终端观察at调度 的执行效果;
切换到第一个虚拟终端观察at调度的执行效果(5分钟后系统将执行重 启调度任务)。
[操作要求2] 设置一个调度,要求1分钟后执行文件job中的作业。 文件job的内容为: find /home/jkx/ -name “*.c” > /home/jkx/fdresult wall user jkx, all code files have been searched out! Please check out. [操作步骤]
续表
守 护 进 程 innd Usenet新闻服务器 功 能 说 明
linuxconf
lpd named netfs network
允许使用本地WEB服务器作为用户接口来配置机器
打印服务器 DNS服务器 安装NFS、Samba和NetWare网络文件系统 激活已配置网络接口的脚本程序
nfsd
portmap postgresql routed sendmail
事件(例如xinetd和lpd)
启动守护进程有如下几种方法
在引导系统时启动 人工手动从shell提示符启动
系统启动script的执行期间 被启动(/etc/rc.d) 任何具有相应执行 权限的用户
使用crond守护进程启动
执行at命令启动
守护进程一般由系统在开机时通过脚本或root
电大操作系统实验报告3_ 进程管理实验

电大操作系统实验报告3_ 进程管理实验电大操作系统实验报告 3 进程管理实验一、实验目的进程管理是操作系统的核心功能之一,本次实验的目的是通过实际操作和观察,深入理解进程的概念、状态转换、进程调度以及进程间的通信机制,掌握操作系统中进程管理的基本原理和方法,提高对操作系统的整体认识和实践能力。
二、实验环境本次实验使用的操作系统为 Windows 10,编程语言为 C 语言,开发工具为 Visual Studio 2019。
三、实验内容及步骤(一)进程的创建与终止1、编写一个 C 程序,使用系统调用创建一个子进程。
2、在父进程和子进程中分别输出各自的进程 ID 和父进程 ID。
3、子进程执行一段简单的计算任务,父进程等待子进程结束后输出结束信息。
以下是实现上述功能的 C 程序代码:```cinclude <stdioh>include <stdlibh>include <unistdh>int main(){pid_t pid;pid = fork();if (pid < 0) {printf("创建子进程失败\n");return 1;} else if (pid == 0) {printf("子进程:我的进程 ID 是%d,父进程 ID 是%d\n",getpid(), getppid());int result = 2 + 3;printf("子进程计算结果:2 + 3 =%d\n", result);exit(0);} else {printf("父进程:我的进程 ID 是%d,子进程 ID 是%d\n",getpid(), pid);wait(NULL);printf("子进程已结束\n");}return 0;}```编译并运行上述程序,可以观察到父进程和子进程的输出信息,验证了进程的创建和终止过程。
(二)进程的状态转换1、编写一个 C 程序,创建一个子进程,子进程进入睡眠状态一段时间,然后被唤醒并输出状态转换信息。
进程管理实验报告_共10篇 .doc

★进程管理实验报告_共10篇范文一:_进程管理实验报告进程管理实验报告一、进程与线程1.实验目的:1.通过本实验学习Linux中创建进程的方法。
2.学习系统调用fork的使用方法。
3.学习系统调用exec族调用的使用方法。
2.实验准备1.进程的创建创建一个进程的系统调用很简单,只要调用fork函数就可以了。
#includepid_tfork();当一个进程调用了fork以后,系统会创建一个子进程,这个子进程和父进程是不同的地方只有它的进程ID和父进程ID,其他的都一样,就像父进程克隆(clone)自己一样,当然创建两个一模一样的进程是没有意义的,为了区分父进程和子进程,我们必须跟踪fork调用返回值。
当fork调用失败的时候(内存不足或者是用户的最大进程数已到)fork返回—1,否则fork的返回值有重要的作用。
对于父进程fork返回子进程ID,而对于fork 子进程返回0,我们就是根据这个返回值来区分父子进程的。
2.关于fork的说明使用该函数时,该函数被调用一次,但返回两次,两次返回的区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。
将子进程ID返回给父进程的理由是:因为一个进程的子进程可以多于一个,所以没有一个函数可以是一个子进程获得其所有子进程的进程ID。
而fork函数使子进程得到的返回值是0的理由是:一个子进程只会有一个父进程,所以子进程总是可以调用函数getpid获得其父进程的进程ID。
3.系统调用exec族调用的说明父进程创建子进程后,子进程一般要执行不同的程序。
为了调用系统程序,我们可以使用系统调用exec族调用。
Exec族调用有以下五个函数:intexecl(constchar*path,constchar*arg,?);intexeclp(constchar*file,constchar*arg,?);intexecle(constchar*path,constchar*arg,?);intexecv(constchar*path,constchar*argv[]);intexecvp(constchar*file,constchar*argv[]);exec族调用可以执行给定程序。
进程管理实验报告分析(3篇)

第1篇一、实验背景进程管理是操作系统中的一个重要组成部分,它负责管理计算机系统中所有进程的创建、调度、同步、通信和终止等操作。
为了加深对进程管理的理解,我们进行了一系列实验,以下是对实验的分析和总结。
二、实验目的1. 加深对进程概念的理解,明确进程和程序的区别。
2. 进一步认识并发执行的实质。
3. 分析进程争用资源的现象,学习解决进程互斥的方法。
4. 了解Linux系统中进程通信的基本原理。
三、实验内容1. 使用系统调用fork()创建两个子进程,父进程和子进程分别显示不同的字符。
2. 修改程序,使每个进程循环显示一句话。
3. 使用signal()捕捉键盘中断信号,并通过kill()向子进程发送信号,实现进程的终止。
4. 分析利用软中断通信实现进程同步的机理。
四、实验结果与分析1. 实验一:父进程和子进程分别显示不同的字符在实验一中,我们使用fork()创建了一个父进程和两个子进程。
在父进程中,我们打印了字符'a',而在两个子进程中,我们分别打印了字符'b'和字符'c'。
实验结果显示,父进程和子进程的打印顺序是不确定的,这是因为进程的并发执行。
2. 实验二:每个进程循环显示一句话在实验二中,我们修改了程序,使每个进程循环显示一句话。
实验结果显示,父进程和子进程的打印顺序仍然是随机的。
这是因为并发执行的进程可能会同时占用CPU,导致打印顺序的不确定性。
3. 实验三:使用signal()捕捉键盘中断信号,并通过kill()向子进程发送信号在实验三中,我们使用signal()捕捉键盘中断信号(按c键),然后通过kill()向两个子进程发送信号,实现进程的终止。
实验结果显示,当按下c键时,两个子进程被终止,而父进程继续执行。
这表明signal()和kill()在进程控制方面具有重要作用。
4. 实验四:分析利用软中断通信实现进程同步的机理在实验四中,我们分析了利用软中断通信实现进程同步的机理。
操作系统实验报告----进程管理

实验内容:进程管理一、实验目的1、掌握Linux中进程的创建方法及执行情况;2、加深对进程、进程树等概念的理解;3、掌握Linux中如何加载子进程自己的程序;4、掌握父进程通过创建子进程完成某项任务的方法;5.、掌握系统调用exit()和_exit()调用的使用。
6、分析进程竞争资源的现象,学习解决进程互斥的方法;进一步认识并发执行的实质二、实验内容(一)进程的创建1、编写一段程序,使用系统调用fork( )创建两个子进程。
当此程序运行时,在系统中有一个父进程和两个子进程活动。
让每一个进程在屏幕上显示一个字符。
#include<stdio.h>main(){int p,x;p=fork();if(p>0){x=fork();if(x>0)printf("father\n");elseprintf("child2");}elseprintf("child1");}输出结果:child1child2father2、运行以下程序,分析程序执行过程中产生的进程情况。
#include <stdio.h>main(){int p,x;p=fork();if (p>0)fork();else{fork();fork();}sleep(15);}实验步骤:编译连接gcc –o forktree forktree.c后台运行./forktree &使用pstree –h 查看进程树运行结果:├─gnom e-terminal─┬─bash─┬─forktree─┬─forktree─┬─forkt ree───forktree││││└─forktree│││└─forktree││└─pstree 分析:程序运行,系统首先创建一个进程forktree,执行到p=fork()创建一个子进程forktree,子进程获得处理机优先执行,父进程等待;执行else,当执行到第一个fork()函数时,子进程创建了一个进程forktree,称之为孙进程,孙进程获得处理机往下执行,子进程等待;执行到第二个fork()函数时,孙进程又创建一个进程forktree,称之为重孙进程,重孙进程很快执行完,将处理机还给孙进程,孙进程很快执行完,将处理机还给子进程;子进程继续往下执行,执行到第二个fork()函数,又创建一个进程forktree,称之为第二孙进程,并获得处理机执行,此进程很快执行完,将处理机还给子进程,子进程也很快执行完,将处理机还给父进程,父进程P>0执行if语句,运行fork()函数,又创建一个进程forktree,称之为第二子进程,此进程获得处理机执行很快运行完,将处理机还给父进程,父进程运行sleep(15)语句,休眠15秒,用pstree命令查询进程树。
进程的管理实验报告

一、实验目的1. 理解进程的基本概念和进程状态转换过程。
2. 掌握进程创建、进程同步和进程通信的方法。
3. 了解进程调度算法的基本原理和实现方法。
4. 通过实验加深对进程管理的理解,提高操作系统实践能力。
二、实验环境1. 操作系统:Linux2. 编程语言:C/C++3. 开发工具:GCC三、实验内容1. 进程创建与状态转换(1)使用fork()函数创建一个子进程,并观察父进程和子进程的进程ID。
(2)使用exec()函数替换子进程的映像,实现进程的创建。
(3)观察进程状态转换过程,如创建、运行、阻塞、就绪、终止等。
2. 进程同步(1)使用互斥锁(mutex)实现进程的互斥访问共享资源。
(2)使用信号量(semaphore)实现进程的同步,如生产者-消费者问题。
(3)观察进程同步的效果,确保进程安全执行。
3. 进程通信(1)使用管道(pipe)实现进程间的单向通信。
(2)使用消息队列(message queue)实现进程间的双向通信。
(3)使用共享内存(shared memory)实现进程间的快速通信。
(4)观察进程通信的效果,确保数据正确传递。
(1)实现基于优先级的进程调度算法,如先来先服务(FCFS)和最高优先级优先(HPF)。
(2)实现基于时间片的轮转调度算法(RR)。
(3)观察进程调度算法的效果,分析不同算法的优缺点。
四、实验步骤1. 编写程序实现进程创建与状态转换,使用fork()和exec()函数。
2. 编写程序实现进程同步,使用互斥锁和信号量。
3. 编写程序实现进程通信,使用管道、消息队列和共享内存。
4. 编写程序实现进程调度,使用优先级调度和时间片轮转调度。
5. 编译并运行程序,观察实验结果,分析实验现象。
五、实验结果与分析1. 进程创建与状态转换通过实验,我们成功创建了父进程和子进程,并观察到进程ID的变化。
在进程创建过程中,父进程的进程ID与子进程的进程ID不同,说明子进程是独立于父进程的实体。
实验三 windows任务管理器的进程管理

五、实验总结:
通过这次实验,让我对Windows XP的任务管理器有了很好的了解,也知道了如何通过选项卡控制各个进程的优先级。刚开始实验时只知道如何打开Windows XP的任务管理器,但对于Windows XP的任务管理器中各个选项卡的运用不是很熟悉。
要查看正在运行程序的优先级,可单击“进程选项卡”,单击“查看”菜单,单击“选择列”>“基于优先级”命令,然后单击“确定”。
为更改正在运行程序的优先级,可在“进程”选项卡上右键单击你要更改的程序,指向“设置优先级”然后单击所需的选项。
更改进程的优先级,可以使其运行的更快或更慢,但也可能对其他进程的性能有相反的影响。
system
00
7,392k
Smss.exe
system
00
448k
Svchost.exe
system
00
5,008k
SOSOUpdate.exe
system
00
5,184k
mDNSResponder..
system
00
3,816k
360rps.exe
system
00
7,000k
ati2evxx.exe
这次试验主要是在老师和同学的帮助下完成的,让我对Windows XP的任务管理器有了一个初步的了解,感到这门课十分神奇。
六、指导教师评语及成绩:
教室签名
成绩
批阅日期
00
33,468k
Ctfmon.exe
Administrator
00
5,244k
360tray.exe
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验三进程管理一、实验目的1.熟悉和理解进程和进程树的概念,掌握有关进程的管理机制2.通过进程的创建、撤销和运行加深对进程并发执行的理解3.明确进程与程序、并行与串行执行的区别4.掌握用C 程序实现进程控制的方法二、实验学时2学时三、实验背景知识所涉及的系统调用1、exec( )系列(exec替换进程映像)系统调用exec( )系列,也可用于新程序的运行。
fork( )只是将父进程的用户级上下文拷贝到新进程中,而exec( )系列可以将一个可执行的二进制文件覆盖在新进程的用户级上下文的存储空间上,以更改新进程的用户级上下文。
exec( )系列中的系统调用都完成相同的功能,它们把一个新程序装入内存,来改变调用进程的执行代码,从而形成新进程。
如果exec( )调用成功,调用进程将被覆盖,然后从新程序的入口开始执行,这样就产生了一个新进程,新进程的进程标识符id 与调用进程相同。
exec( )没有建立一个与调用进程并发的子进程,而是用新进程取代了原来进程。
所以exec( )调用成功后,没有任何数据返回,这与fork( )不同。
exec( )系列系统调用在UNIX系统库unistd.h中,共有execl、execlp、execle、execv、execvp五个,其基本功能相同,只是以不同的方式来给出参数。
#include<unistd.h>int execl(const cha r *pathname, const char *arg, …);int execlp(const char *, const char *arg, …);int execle(const char *pathname, const char *arg, …, const char *envp[ ]);int execv(const char *pathname, char *const argv[ ]);int execvp(const char *, char *const argv[ ]);参数:path参数表示你要启动程序的名称包括路径名。
arg参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束。
返回值:成功返回0,失败返回-1注:上述exec系列函数底层都是通过execve系统调用实现.1)带l 的exec函数:execl,execlp,execle,表示后边的参数以可变参数的形式给出且都以一个空指针结束。
#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(void){printf("entering main process---\n");execl("/bin/ls","ls","-l",NULL);printf("exiting main process ----\n");return 0;运行结果:利用execl将当前进程main替换掉,所有最后那条打印语句不会输出。
2)带 p 的exec函数:execlp,execvp,表示第一个参数path不用输入完整路径,只有给出命令名即可,它会在环境变量PATH当中查找命令#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(void){printf("entering main process---\n");if(execl("ls","ls","-l",NULL)<0)// if(execlp("ls","ls","-l",NULL)<0)perror("excl error");return 0;}结果不能替换,因没有指定路径名。
若将蓝色语句换成红色部分内容执行,则可以替换成功。
3)不带 l 的exec函数:execv,execvp表示命令所需的参数以char *arg[]形式给出且arg 最后一个元素必须是NULL。
#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(void){printf("entering main process---\n");int ret;char *argv[] = {"ls","-l",NULL};ret = execvp("ls",argv);if(ret == -1)perror("execl error");printf("exiting main process ----\n");return 0;}4)带 e 的exec函数:execle表示,将环境变量传递给需要替换的进程2、exec( )和fork( )联合使用系统调用exec和fork( )联合使用能为程序开发提供有力支持。
用fork( )建立子进程,然后在子进程中使用exec( ),这样就实现了父进程与一个与它完全不同子进程的并发执行。
一般,wait、exec联合使用的模型为:int status;............if (fork( )= =0){...........;execl(...);...........;}wait(&status);3、wait()当一个子进程先于父进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行,或者父进程调用了wait才告终止。
子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态。
进程表中代表子进程的数据项是不会立刻释放的,虽然不再活跃了,可子进程还停留在系统里,因为它的退出码还需要保存起来以备父进程中后续的wait调用使用。
它将称为一个“僵进程”。
•调用wait函数查询子进程退出状态,若子进程没有运行完,则父进程会被挂起,等待子进程运行结束。
如果子进程没有完成,父进程一直等待。
wait( )将调用进程挂起,直至其子进程因暂停或终止而发来软中断信号为止。
如果在wait( )前已有子进程暂停或终止,则调用进程做适当处理后便返回。
系统调用格式:#include<sys/types.h>#include<sys/wait.h>int wait(status)int *status;其中,status是用户空间的地址。
它的低8位反应子进程状态,为0表示子进程正常结束,非0则表示出现了各种各样的问题;高8位则带回了exit( )的返回值。
exit( )返回值由系统给出。
当一个进程结束时,Linux 系统将产生一个SIGCHLD 信号通知其父进程。
在父进程未查询子进程结束的原因时,该子进程虽然停止了,但并未完全结束。
此时这一子进程被称为僵尸进程(zombie process)。
例如,在有些情况下父进程先于子进程退出,于是会看到在系统提示符“$”后子进程仍然在连续输出信息,这对用户是非常不友好的。
我们可以使用系统调用wait,来让父进程处于等待状态,直到子进程退出后才继续执行后面的语句。
参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。
但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:pid = wait(NULL);如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1。
如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的,以及正常结束时的返回值,或被哪一个信号结束的等信息。
由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏(macro)来完成这项工作,4、exit()进程结束可通过相应的函数实现:#include<stdlib.h>void exit(int status); //终止正在运行的程序,关闭所有被该文件打开的文件描述符。
其中,status是返回给父进程的一个整数,以备查考。
int atexit(void (*function)(void)); //用于注册一个不带参数也没有返回值的函数以供程序正常退出时被调用。
参数function 是指向所调用程序的文件指针。
调用成功返回0,否则返回-1,并将errno 设置为相应值int on_exit(void (*function)(int,void *),void *arg); //作用与atexit 类似,不同是其注册的函数具有参数,退出状态和参数arg 都是传递给该函数使用void abort(void); //用来发送一个SIGABRT 信号,该信号将使当前进程终止WIFEXITED(status):这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。
WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真终止进程的执行。
为了及时回收进程所占用的资源并减少父进程的干预,UNIX/LINUX利用exit( )来实现进程的自我终止,通常父进程在创建子进程时,应在进程的末尾安排一条exit( ),使子进程自我终止。
exit(0)表示进程正常终止,exit(1)表示进程运行有错,异常终止。
如果调用进程在执行exit( )时,其父进程正在等待它的终止,则父进程可立即得到其返回的整数。
核心须为exit( )完成以下操作:(1)关闭软中断(2)回收资源(3)写记帐信息(4)置进程为“僵死状态”四、实验内容1.了解系统调用(execl,execlp,execle,execv,execvp)使用例3.1:创建一个进程,并使用exec将其替换成其他程序的进程#include<stdio.h>#include<unistd.h> #include<stdlib.h> main( ){int pid;pid=fork( ); switch(pid){case -1:printf("fork fail!\n"); exit(1); case 0:printf(“Child process PID:%d.\n”,g etpid()); execl("/bin/ls","ls","-1",NULL);printf("exec fail!\n");exit(1);default:printf(“Parent process PID: %4d.\n”,getpid()); wait(NULL);printf("ls completed !\n");exit(0);}}运行程序并回答以下问题:问题1:该程序中一共有几个进程并发?问题2:程序的运行结果为什么含义?2.父进程查询子进程的退出例3.2:#include<sys/types.h>#include<stdio.h>#include<sys/wait.h>#include<unistd.h>#include<stdlib.h>int main(){pid_t pid; char *message; int n; printf(“Fork program starting\n”); pid=fork(); switch(pid){case -1:printf(“Fork error!\n”); exit(1);case 0:message= “Child process is printing.”; n=5;break;default:message=“Parent process id printing.”; n=3;break;}for(;n>0;n--){ puts(message); sleep(1);} if(pid){pid_t child_pid; int stat_val;child_pid=wait(&stat_val); printf(“Child has finished: PID=%d.\n”,child_pid);}exit(0);}运行程序并回答以下问题:问题1:该程序如果不加红色代码部分其运行结果是什么?为什么结果会出现在下一行的提示符“#”或“$”后?问题2:添加红色部分程序的作用是什么?例3.3:#include<stdio.h>#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>#include<stdlib.h>main(){int status; pid_t pc,pr;pc=fork();if(pc<0)printf("error ocurred!\n");else if(pc==0){printf("This is child process with pid of %d.\n",getpid());exit(3); }else{pr=wait(&status);if(WIFEXITED(status)){printf("the child process %d exit normally.\n",pr);printf("the return codeis %d.\n",WEXITSTATUS(status)); }elseprintf("the child process %d exit abnormally.\n",pr); }}运行程序并回答以下问题:问题1:该程序红色代码部分的含义是什么?问题2:程序的运行结果是什么,理解wait函数的作用?3.进程的终止例3.4:#include<sys/types.h>#include<sys/wait.h>#include<stdio.h>#include<stdlib.h>void h_exit(int status); static void forkerror(void); static void waiterror(void); int main(void){pid_t pid; int status; if(pid=fork()<0) atexit(forkerror);else if(pid==0) abort();if(wait(&status)!=pid) atexit(waiterror);h_exit(status);}void h_exit(int status){if(WIFEXITED(status))printf(“Normal termination, exit status=%d.\n”, WEXITSTATUS(status));else if(WIFSIGNALED(status)) printf(“Abnormal termination, exit status=%d.\n”, WEXITSTATUS(status));} void forkerror(void){ printf(“Fork error!\n”); } void waiterror(void) { printf(“Wait error!\n”);}运行程序并回答以下问题:问题1:该程序的含义是什么?问题2:程序的运行结果是什么,请解释h_exit函数的作用?4.了解system 函数的用法用户可以使用该函数来在自己的程序中调用系统提供的各种命令。