实验实验报告
一、实验名称
进程创建与通信
二、实验目的
(1) 加深对进程概念的理解,理解进程和程序的区别。
(2) 认识并发进程的实质。分析进程争用资源的现象,学习解决进程互斥的方法。
(3) 认识并发进程的软中断通信。掌握使用软中断控制进程的编程技术
(4) 掌握管道通信原理。
(5) 通过编写 Linux 消息发送和接收程序,了解和熟悉 Linux 消息通信机制
(6) 通过编写共享存储区的通信程序,理解 Linux 共享存储区机制。
、实验环境
(1) 硬件环境:Intel Pentium III 以上 CPU128MB 以上内存,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语句判别是在子进程中还是在父进程中。程序运行后会创建三个进程,它们分别是子进程 p1p2、父进程。这三个进程并发运行。假定子进程有些任务要做,完成这些任务要花一定时间,因此可以用一个延时函数简单地模拟这些任务。
(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()p1p2 发自定义信号。 然后用 wait()等待 p1p2 结束。p1p2 signal()捕捉父进程的自定义信号(信号1617),捕到后显示信息,然后用 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管道进行进程间单条信息的通信
九、教师评阅意见