admin 管理员组

文章数量: 1184232


2024年1月14日发(作者:csdn官网电脑版)

实验二实验报告

一、实验名称

进程创建与通信

二、实验目的

(1) 加深对进程概念的理解,理解进程和程序的区别。

(2) 认识并发进程的实质。分析进程争用资源的现象,学习解决进程互斥的方法。

(3) 认识并发进程的软中断通信。掌握使用软中断控制进程的编程技术

(4) 掌握管道通信原理。

(5) 通过编写 Linux 消息发送和接收程序,了解和熟悉 Linux 消息通信机制

(6) 通过编写共享存储区的通信程序,理解 Linux 共享存储区机制。

三、实验环境

(1) 硬件环境:Intel Pentium III 以上 CPU,128MB 以上内存,2GB 以上硬盘

(2) 软件环境:Ubuntu 13.04 操作系统。

注:Ubuntu 是 Linux 的一个免费发行的流行版本,系英国"科能软件股份有限公司

(Canonical)"开发

四、实验内容

(1)编写一段程序,使用系统调用 fork()创建两个子进程 p1 和 p2。p1 显示字符'b',p2

显示字符'c',父进程显示字符'a',父进程和两个子进程并发运行。观察并记录屏幕上的

显示结果,分析原因。(进程创建e202.c)

(2)编写一段程序, 实现下列功能:创建两个子进程。用户按中断键(Ctrl-C),被父进程捕

获。父进程捕获用户中断键后向子进程发信号,让两个子进程显示下列信息后终止:

Child process 1 is killed by parent!

Child process 2 is killed by parent!

父进程等待两个子进程终止后,自己输出下列信息后终止:

Parent process is killed!(进程控制e203.c)

(e800.c)

(3) 编写一段程序, 实现进程的管道通信。使用系统调用 fork()创建两个子进程 p1 和

p2。

使用系统调用 pipe()建立一条管道。两个子进程 p1 和 p2 分别向管道各写一句话:

child 1 is sending a message!

child 2 is sending a message!

父进程从管道中读出来自两个子进程的信息,显示在屏幕上。(进程通信 e204.c)

五、实验原理

(1) 编译与连接程序

Linux 下常用的 C/C++语言编译器是 GCC(GNU Compiler Collection),它是

GNU 项目中符合 ANSI C 标准的编译系统,能够编译 C、C++等语言编写的程序。它除了功能强大,结构灵活外,还可以通过不同的前端模块来支持各种语言,如 Java、Pascal

等。GCC 编译过程分为四个阶段:预处理(Pre-Processing)编译(Compiling)汇编

(Assembling)连接(Linking)

(2)fork()系统调用

fork 的功能是创建子进程。调用 fork 的进程称为父进程

(3)wait()系统调用

wait 的功能是等待子进程结束。发出 wait 调用的进程只要有子进程,就会睡眠

直到子进程中的一个终止为止。若没有子进程,则该调用立即返回

(4)信号是一种软件中断,用来通知进程发生了异步事件。进程之间可以互相通过系统调用

kill 发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个

事件。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。

(5)所谓管道,就是将一个进程的标准输出与另一个进程的标准输入联系在一起,是进程通信的一种方法。创建管道使用系统调用 pipe()。管道两端是固定了任务的,一端只能用于读,另一端只能用于写。

(6)程序的并发执行具有随机性和不可再现性。程序并发执行会导致资源共享和资源竞争,各程序向前执行的速度会受资源共享的制约。程序的动态执行过程用进程这个概念来描述。由于向前推进的速度不可预知,所以多个进程并发地重复执行,整体上得到的结果可能不同。但要注意,就其中某单个进程而言,其多次运行结果是确定的。

(7)并发运行的进程之间,可以通过信号进行同步,也可以通过管道、消息通信机制、共享存储机制进行通信。

六、实验步骤

【任务 1】

编写一段程序,使用系统调用 fork()创建两个子进程 p1 和 p2。

P1显示字符'b',p2显示字符'c',父进程显示字符'a',父进程和两个子进程并发运行。观察并记录屏幕上的显示结果,分析原因。

(1)程序设计

用while 语句控制 fork()直到创建成功。用if语句判别是在子进程中还是在父进程中。程序运行后会创建三个进程,它们分别是子进程 p1、p2、父进程。这三个进程并发运行。假定子进程有些任务要做,完成这些任务要花一定时间,因此可以用一个延时函数简单地模拟这些任务。

(2)上机操作

用 gedit 输入上述源代码,取名为 e202.c。

编译

gcc –o e202 e202.c

运行

./e202

按向上的光标键、回车,运行刚才的程序。快速重复这个步骤,观察并记录结果。

(3)问题

屏幕上是否有时显示 bac,有时显示 bca,…。为什么会这样呢?

【任务 2】

编写一段程序, 实现下列功能:创建两个子进程。用户按中断键(Ctrl-C),被父进程捕

获。父进程捕获用户中断键后向子进程发信号,让两个子进程显示下列信息后终止:

Child process 1 is killed by parent!

Child process 2 is killed by parent!

父进程等待两个子进程终止后,自己输出下列信息后终止:

Parent process is killed!

(1)参考程序

使用 while 语句控制 fork()创建两个字进程 p1 和 p2。父进程使用系统调用

signal()捕捉用户的中断键。若用户按下中断键 Ctrl-C,则用 kill()向 p1、p2

发自定义信号。 然后用 wait()等待 p1、p2 结束。p1、p2 用 signal()捕捉父进程的自定义信号(信号16和17),捕到后显示信息,然后用 exit()终止自己。

(2)上机操作

用 gedit 输入源代码。

编译、运行,观察屏幕,记录结果。

(3) 问题:为什么两个子进程没有显示预期的信息?

【任务 3】

编写一段程序, 实现进程的管道通信。使用系统调用 fork()创建两个子进程 p1 和 p2。

使用系统调用 pipe()建立一条管道。两个子进程 p1 和 p2 分别向管道各写一句话:

child 1 is sending a message!

child 2 is sending a message!

父进程从管道中读出来自两个子进程的信息,显示在屏幕上。

(2)上机操作

编辑、编译、运行、观察屏幕、记录结果。

七、实验结果

【任务 1】

#include

void delay(int x) //延时函数

{

int i,j;

for(i=0;i

for(j=0;j

}

int main()

{

int p1,p2;

while((p1=fork())==-1); //创建子进程 p1

if(p1==0) //子进程 p1 创建成功

{

delay(10); //子进程 p1 延时

putchar('b'); //子进程 p1 显示字符'b'

}else{

while((p2=fork())==-1); //创建子进程 p2

if(p2==0) //子进程 p2 创建成功

{

delay(10); //子进程 p2 延时

putchar('c'); //子进程 p2 显示字符'c'

}else{

delay(100); //父进程延时

putchar('a'); //父进程显示字符'a'

} }

return 0;

}

实验截图:

可以看出,以a,b为开头的a,b,c的各种组合都可能会出现,为什么没有出现c开头?

因为内存资源是随机分配且独占的,只有当一个进程运行结束才会运行下一个进程。

只能ab为开始的原因是当p1创建时,父进程和p1抢占资源。得到资源的一个先运行结束,所以只能是a或者b为开头。另一个再和进程p2争夺资源,输出另外两个字母。

从多次结果来看,资源的分配优先概率a>b>c

任务2:

#include

#include

#include

void waiting();

void stop();

int wait_mark;

int main() {

int p1, p2;

while ((p1 = fork()) == -1)

;

if (p1 > 0) {

while ((p2 = fork()) == -1)

;

if (p2 > 0) {

printf("parentn");

wait_mark = 1;

signal(SIGINT, stop);

waiting();

kill(p1, 16);

kill(p2, 17);

wait(0);

wait(0);

printf("parent process is kill!n");

exit(0);

} else {

printf("p2n");

signal(SIGINT,SIG_IGN);

wait_mark = 1; //置等待标志

signal(17, stop); //捕捉父进程信号 17,调用信号处理函数 stop()

waiting(); //忙等待

lockf(stdout, 1, 0); //锁住标准输出 stdout

printf("child process 2 is killed by prent!n");

lockf(stdout, 0, 0); //解锁

exit(0); //子进程 p2 结束自己

}

} else {

printf("p1n");

signal(SIGINT,SIG_IGN);

wait_mark = 1; //置等待标志

signal(16, stop); //捕捉父进程信号 16,调用信号处理函数 stop()

waiting(); //忙等待

lockf(stdout, 1, 0); //锁住标准输出 stdout

printf("child process 1 is killed by parent!n");

lockf(stdout, 0, 0); //解锁

exit(0); //子进程 p2 结束自己

}

return 0;

}

void waiting() //忙等待函数

{

while(wait_mark!=0); //忙等待

}

void stop()

{

wait_mark=0; //清除忙等待标志

}

实验截图:

两个子进程和父进程都显示了预期的信息

任务3:

#include

#include

#include

int pid1, pid2;

int main()

{

int fd[2];

char OutPipe[100], InPipe[100];

pipe(fd);

while((pid1 = fork()) == -1);

if(pid1 == 0){

printf("p1n");

lockf(fd[1], 1, 0);

sprintf(OutPipe, "Child 1 process is sending a message!");

write(fd[1], OutPipe, 50);

sleep(1);

lockf(fd[1], 0, 0);

exit(0);

}else{

while((pid2 = fork()) == -1);

if(pid2 == 0){

printf("p2n");

lockf(fd[1], 1, 0);

sprintf(OutPipe, "Child 2 process is sending a message!");

write(fd[1], OutPipe, 50);

sleep(1);

lockf(fd[1], 0, 0);

exit(0);

}else{

printf("parentn");

wait(0);

read(fd[0], InPipe, 50);

printf("%sn", InPipe);

wait(0);

read(fd[0], InPipe, 50);

printf("%sn", InPipe);

exit(0);

}

}

return 0;

}

编辑、编译、运行、观察屏幕、记录结果。

实验截图:

八、实验总结

任务一:

1.学会了linux下使用gcc编译器对c语言程序编译:gcc -o (文件名) (新文件名)。

2.学会进程的基本概念和运行方式,通过fork()新建子进程,并从父程序的fork()下一行开始运行,且返回值为0

任务二:

1. 通过调用signer函数,通过监听信号对进程进行操作。需要注意的是,所有的进程都会监听信号,需要把无需听某个信号的进程进行处理防止意外退出。

任务三:

1. 学习进程间通信的基本理论和大致方法

2. 通过pipe管道进行进程间单条信息的通信

九、教师评阅意见


本文标签: 进程 程序 运行 信号 调用