实验二实验报告
一、实验名称 进程创建与通信 |
二、实验目的 (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 <stdio.h> void delay(int x) //延时函数 { int i,j; for(i=0;i<x;i++) for(j=0;j<x;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 <signal.h> #include <stdio.h> #include <unistd.h> 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("parent\n"); 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("p2\n"); 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("p1\n"); 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 <unistd.h> #include <stdio.h> #include <signal.h> int pid1, pid2; int main() { int fd[2]; char OutPipe[100], InPipe[100]; pipe(fd); while((pid1 = fork()) == -1); if(pid1 == 0){ printf("p1\n"); 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("p2\n"); 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("parent\n"); wait(0); read(fd[0], InPipe, 50); printf("%s\n", InPipe); wait(0); read(fd[0], InPipe, 50); printf("%s\n", InPipe); exit(0); } } return 0; } 编辑、编译、运行、观察屏幕、记录结果。 实验截图: |
八、实验总结 任务一: 1.学会了linux下使用gcc编译器对c语言程序编译:gcc -o (文件名) (新文件名)。 2.学会进程的基本概念和运行方式,通过fork()新建子进程,并从父程序的fork()下一行开始运行,且返回值为0 任务二: 1.通过调用signer函数,通过监听信号对进程进行操作。需要注意的是,所有的进程都会监听信号,需要把无需听某个信号的进程进行处理防止意外退出。 任务三: 1.学习进程间通信的基本理论和大致方法 2.通过pipe管道进行进程间单条信息的通信 |
九、教师评阅意见 |
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论