操作系统:实验三进程间通信实验

一、实验目的

1、了解什么是信号。

2、熟悉LINUX系统中进程之间软中断通信的基本原理。

3、理解进程的同步关系。

4、掌握用信号实现进程间的同步操作。

5、了解什么是管道。

6、熟悉UNIX/LINUX支持的管道通信方式。

、实验内容

1、阅读下列程序,执行程序并给出结果截屏,分析程序功能。(2分)

(1)

#include<unistd.h>

#include<signal.h>

#include<stdio.h>

#include<stdlib.h>

#include<sys/types.h>

#include<sys/wait.h>

int k;

void func()

{   k=0; }

main()

{   int p;

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

        if(p==0) //子进程

        {   k=1;

           signal(10,func);

           while(k!=0);

           printf("Child process is killed by parent!\n");

        }

        else //父进程

        {   sleep(1);

           kill(p,10);

           wait(0);

           printf("Parent process has killed!\n");

        }  

}

编译及执行过程和结果截屏:

程序实现功能(父进程和子进程分别做了什么?):

父进程先执行,创建子进程后休眠 1 秒,然后向子进程发送信号,并等待子进程退出。在这种情况下,子进程接收到信号后,执行 func() 函数,将 k 置为 0,然后打印 "Child process is killed by parent!"。随后父进程继续执行,打印 "Parent process has killed!"。

子进程先执行,在接收到信号后执行 func() 函数,将 k 置为 0,然后退出。父进程接着执行,休眠 1 秒,然后尝试向已经退出的子进程发送信号,但是由于子进程已经退出,所以 kill() 函数失败。父进程继续执行,打印 "Parent process has killed!"。

父子进程竞争情况下,可能会出现不确定的结果。例如,父进程发送信号前子进程已经退出,这种情况下父进程会等待一个不存在的进程退出,可能会产生错误。

(2)

#include<unistd.h>

#include<signal.h>

#include<stdio.h>

int k=1;

void func()

{   k=0; }

main()

{

        signal(SIGINT,func);

        while(k!=0);

        printf("\n main process is killed by keybreak!\n");

}

编译及执行过程和运行结果截屏:

解释程序实现的功能:

1. 在程序运行期间,通过 `signal(SIGINT,func)` 函数调用注册了一个信号处理函数 `func()`。这意味着当程序接收到 `SIGINT` 信号时(通常由键盘输入 Ctrl+C 产生),将执行 `func()` 函数。

2. `func()` 函数被定义为将全局变量 `k` 的值设为 0。这表明当程序接收到 `SIGINT` 信号时,会将 `k` 的值修改为 0。

3. 主函数中的 `while(k!=0)` 循环会不断检查 `k` 的值是否为 0。如果 `k` 的值变为 0(即接收到了 `SIGINT` 信号),则循环结束。

4. 当循环结束时,程序会打印一条消息:"main process is killed by keybreak!"。这条消息表明主进程由于键盘输入而被终止。

因此,该程序的功能是当用户在程序运行过程中按下 Ctrl+C 时,主进程会接收到 `SIGINT` 信号,执行 `func()` 函数将全局变量 `k` 的值设为 0,然后退出循环并打印提示消息,程序结束运行。

2、编写一段程序,使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断信号(即按ctrl+c键),当捕捉到中断信号后,父进程用系统调用kill()向两个子进程发出信号,子进程捕捉到信号后,分别输出下列信息后终止: 

                       Child process 1 is killed by parent!

                       Child process 2 is killed by parent!

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

                       Parent process is killed!

要求给出编译及执行过程和运行结果截屏。(2分)

#include<unistd.h>

#include<signal.h>

#include<stdio.h>

#include<stdlib.h>

#include<sys/types.h>

#include<sys/wait.h>



int k=1;



void func()

{

    k=0;

}



int main()

{

    signal(SIGINT, func);

    while (k != 0);

    int p1, p2;

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

    if (p1 == 0) // 第一个子进程

    {

        k=1;

        signal(10, func);

        while (k != 0);

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

        exit(0);

    }

    else // 父进程

    {

        sleep(1);

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

        if (p2 == 0) // 第二个子进程

        {

            k=1;

            signal(10, func);

            while (k != 0);

            printf("Child process 2 is killed by parent!\n");

            exit(0);

        }

        else // 父进程

        {

            sleep(1);

            kill(p1, 10);

            kill(p2, 10);

            printf("Parent process is killed!\n");

        }  

    }



    return 0;

}

在父进程中,使用 signal(SIGINT, func) 注册了 SIGINT 信号的处理函数 func,这表示当父进程收到 SIGINT 信号(通常是通过键盘输入 Ctrl+C 产生)时,会将全局变量 k 设为 0。

父进程进入了一个无限循环,等待 k 的值变为 0。这意味着父进程会一直等待直到收到 SIGINT 信号,或者其他方式修改了 k 的值。

父进程创建了两个子进程 p1 和 p2。每个子进程执行类似的操作:在无限循环中等待 k 的值变为 0,然后输出相应的消息并终止。

在父进程中,先创建并杀死了第一个子进程 p1,然后创建并杀死了第二个子进程 p2。之后输出了 "Parent process is killed!" 消息。

为了演示父子进程之间的信号通信。父进程通过键盘输入 Ctrl+C 触发 SIGINT 信号,子进程通过 SIGUSR1 信号接收到父进程的通知,然后输出相应的消息。这种方式可以通过信号来实现进程之间的通信和协作。

3、编写程序,利用信号实现司机售票员同步操作问题。要求给出编译及运行过程和结果截图。(2分)

参考程序框架:

#include<unistd.h>

#include<signal.h>

#include<stdio.h>

#include<stdlib.h>

#include<sys/types.h>

#include<sys/wait.h>

int k;

void func()

{   k=0; }

main()

{   int p;

    int count=3;

    signal(12,func);      

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

    if(p>0) //司机进程

    {   while(count)

        {   k=1;

           sleep(1);

           printf("driver:drive\n");

           printf("driver:stop\n");

               kill(p,10);         //发送信号给售票员

           while(k);

           printf("driver:start\n");

           count--;

        }

    }else //售票员进程

    {   signal(10,func);

        while(count)

        {   k=1;

           printf("conductor:sell ticket.\n");

           while(k);

           printf("conductor:open door.\n");

           printf("conductor:close door.\n");

  int ppid = getppid();     //取得司机进程的识别码

               kill(ppid,12); //发送信号给司机

           count--;

        }

    }

}

编译及执行过程和结果截屏:

1. 首先,在主函数中定义了两个变量 `p` 和 `count`。`p` 用于保存 `fork()` 函数的返回值,`count` 用于表示循环执行的次数。

2. 使用 `signal(12, func)` 注册了信号处理函数 `func()`,该函数在接收到编号为 12 的信号时会将全局变量 `k` 设为 0。编号为 12 的信号在程序中用于司机和售票员之间的通信。

3. 在一个循环中,通过 `fork()` 创建了子进程。父进程中 `p` 的值大于 0,而子进程中 `p` 的值等于 0。

4. 如果是父进程(司机进程),则执行如下操作:

   - 将 `k` 设为 1,表示司机正在驾驶。

   - 打印 "driver:drive",表示司机正在驾驶。

   - 打印 "driver:stop",表示司机停车。

   - 通过 `kill(p, 10)` 向售票员进程发送信号 10(`SIGUSR1`),告知售票员停车。

   - 进入循环,等待售票员处理完毕(`k` 变为 0),然后再次开始驾驶。

5. 如果是子进程(售票员进程),则执行如下操作:

   - 使用 `signal(10, func)` 注册了信号处理函数,该函数在接收到编号为 10 的信号时将 `k` 设为 0。

   - 进入循环,等待司机停车信号。

   - 收到司机停车信号后,将 `k` 设为 1,表示售票员正在处理。

   - 打印 "conductor:sell ticket.",表示售票员正在售票。

   - 发送信号 12(`SIGUSR2`)给司机进程,告知司机售票员已经完成任务。

   - 等待司机开始行驶的信号。

6. 程序最终会按照循环的次数执行上述操作,直到循环结束。

4、编制一段程序,实现进程的管道通信。使用pipe()建立一条管道线。两个子进程p1和p2分别向管道各写一句话:

            Child 1 is sending message!

            Child 2 is sending message!

而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。(1分)

<参考程序>

#include<unistd.h>

#include<signal.h>

#include<stdio.h>

#include<stdlib.h>

int pid1,pid2;

main()

{   int fd[2];

    char OutPipe[100],InPipe[100];

    pipe(fd);

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

    if(pid1==0)

    {   sprintf(OutPipe,"child 1 process is sending message!");

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

        sleep(5);

        exit(0);

    }

    else

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

        if(pid2==0)

        {   sprintf(OutPipe,"child 2 process is sending message!");

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

            sleep(5);

            exit(0);

        }

        else

        {   wait(0);

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

            printf("%s\n",InPipe);

            wait(0);

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

            printf("%s\n",InPipe);

            exit(0);

        }

    }

}

编译及执行过程和运行结果截屏。

这段代码实现了一个父进程创建两个子进程的过程,其中每个子进程都向父进程通过管道发送一条消息,然后父进程接收这两条消息并输出

1. 父进程创建了一个管道 `fd`,用于父子进程之间的通信。

2. 父进程通过 `fork()` 创建了第一个子进程 `pid1`。如果创建失败,则继续尝试创建,直到成功为止。

3. 如果当前进程是第一个子进程,则向管道中写入一条消息 "child 1 process is sending message!",然后睡眠 5 秒后退出。

4. 如果当前进程是父进程,则继续创建第二个子进程 `pid2`,并进行类似的操作。

5. 如果当前进程是第二个子进程,则向管道中写入一条消息 "child 2 process is sending message!",然后睡眠 5 秒后退出。

6. 父进程在两个子进程都退出后,使用 `wait()` 函数等待子进程退出,并通过管道读取两个子进程发送的消息。

7. 父进程读取管道中的消息,并打印输出。

总之该程序通过管道实现了父进程与两个子进程之间的通信,子进程向父进程发送了各自的消息,而父进程则接收并打印输出这些消息。

5、在父进程中用pipe()建立一条管道线,往管道里写信息,两个子进程接收父进程发送的信息。(2分)

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/wait.h>

#include <string.h>



int main() {

    int fd[2];

    pid_t pid1, pid2;

    char message[] = "父进程发送给子进程的消息";



    char buffer[100];



    if (pipe(fd) < 0) {

        perror("管道创建失败");

        exit(EXIT_FAILURE);

    }



    if ((pid1 = fork()) < 0) {

        perror("子进程1创建失败");

        exit(EXIT_FAILURE);

    }



    if (pid1 == 0) {

        // 子进程1

        close(fd[1]); // 关闭管道写端

        read(fd[0], buffer, sizeof(buffer));

        printf("子进程1接收到: %s\n", buffer);

        close(fd[0]); // 关闭管道读端

        exit(EXIT_SUCCESS);

    }



    if ((pid2 = fork()) < 0) {

        perror("子进程2创建失败");

        exit(EXIT_FAILURE);

    }



    if (pid2 == 0) {

        // 子进程2

        close(fd[1]); // 关闭管道写端

        read(fd[0], buffer, sizeof(buffer));

        printf("子进程2接收到: %s\n", buffer);

        close(fd[0]); // 关闭管道读端

        exit(EXIT_SUCCESS);

    }



    // 父进程

    close(fd[0]); // 关闭管道读端

    write(fd[1], message, strlen(message) + 1);

    write(fd[1], message, strlen(message) + 1);

    close(fd[1]); // 关闭管道写端



    wait(NULL);

    wait(NULL);



    return 0;

}

父进程通过管道向两个子进程发送消息,并且两个子进程分别接收并输出这些消息。

1. 创建了一个管道 `fd`,用于父子进程之间的通信。

2. 父进程通过 `fork()` 创建了两个子进程 `pid1` 和 `pid2`。

3. 子进程1中,关闭了管道的写端,然后通过管道的读端接收父进程发送的消息,并打印输出。

4. 子进程2中,同样关闭了管道的写端,然后通过管道的读端接收父进程发送的消息,并打印输出。

5. 父进程中,关闭了管道的读端,然后通过管道的写端向管道中写入两条消息,然后关闭了写端。

6. 父进程使用 `wait()` 函数等待两个子进程退出。

总之该程序通过管道实现了父进程向两个子进程发送消息的功能,而两个子进程分别接收并输出了父进程发送的消息。

6、编程用管道实现父子进程间的双向通信。要求给出设计思路,调试通过的程序和编译执行过程以及结果截屏。(附加题)

设计思路:

父进程创建两个管道,一个用于父进程向子进程发送消息,另一个用于接收子进程发送的消息。父进程创建子进程。子进程同样创建两个管道,一个用于子进程向父进程发送消息,另一个用于接收父进程发送的消息。父进程和子进程可以根据需要进行读取和写入管道。当父进程需要向子进程发送消息时,它将消息写入第一个管道,子进程从该管道读取消息。当子进程需要向父进程发送消息时,它将消息写入第二个管道,父进程从该管道读取消息。父子进程之间需要遵循一定的通信协议,以确保消息的正确传递和处理。

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <string.h>



#define BUFFER_SIZE 100



int main() {

    int parent_to_child[2]; // 父进程向子进程发送消息的管道

    int child_to_parent[2]; // 子进程向父进程发送消息的管道

    pid_t pid;



    // 创建父进程向子进程发送消息的管道

    if (pipe(parent_to_child) == -1) {

        perror("pipe");

        exit(EXIT_FAILURE);

    }



    // 创建子进程向父进程发送消息的管道

    if (pipe(child_to_parent) == -1) {

        perror("pipe");

        exit(EXIT_FAILURE);

    }



    // 创建子进程

    pid = fork();

    if (pid == -1) {

        perror("fork");

        exit(EXIT_FAILURE);

    }



    if (pid == 0) { // 子进程

        close(parent_to_child[1]); // 子进程关闭向子进程发送消息的写端

        close(child_to_parent[0]); // 子进程关闭向父进程发送消息的读端



        // 子进程从父进程发送消息的管道读取消息并输出

        char buffer[BUFFER_SIZE];

        read(parent_to_child[0], buffer, BUFFER_SIZE);

        printf("子进程接收到父进程的消息:%s\n", buffer);



        // 子进程向父进程发送消息

        const char *msg_to_parent = "Hello, parent!";

        write(child_to_parent[1], msg_to_parent, strlen(msg_to_parent) + 1);



        exit(EXIT_SUCCESS);

    } else { // 父进程

        close(parent_to_child[0]); // 父进程关闭向子进程发送消息的读端

        close(child_to_parent[1]); // 父进程关闭向父进程发送消息的写端



        // 父进程向子进程发送消息

        const char *msg_to_child = "Hello, child!";

        write(parent_to_child[1], msg_to_child, strlen(msg_to_child) + 1);



        // 父进程从子进程发送消息的管道读取消息并输出

        char buffer[BUFFER_SIZE];

        read(child_to_parent[0], buffer, BUFFER_SIZE);

        printf("父进程接收到子进程的消息:%s\n", buffer);



        // 等待子进程退出

        wait(NULL);

    }



    return 0;

}

三、实验总结和体会(1分)

实验涉及了进程间通信的不同机制,分别是信号处理和管道通信。通过这些实验,你学习了如何在进程之间传递信息,以及如何利用不同的通信方式实现进程之间的协作和交互

进程间通信: 通过管道、信号等机制,你了解了不同进程间通信的方式和原理。通过这些实验,你掌握了进程间通信的基本概念,并且能够使用这些通信机制在多个进程之间进行数据交换和同步。

信号处理: 你研究了信号的基本概念和处理机制,包括注册信号处理函数、发送信号、处理信号等。通过信号处理,你可以实现进程间的异步通信和事件处理,对于编写并发和异步程序非常重要。

创建管道:使用 pipe() 系统调用创建管道,获取用于通信的文件描述符。

创建子进程:使用 fork() 系统调用创建子进程,使得父子进程共享同一份代码和数据,但各自拥有独立的地址空间。

父子进程间通信:父子进程通过管道进行通信。父进程向管道写入数据,子进程从管道读取数据;子进程向管道写入数据,父进程从管道读取数据。

关闭文件描述符:在通信完成后,需要关闭未使用的文件描述符,以释放资源。

子进程退出:子进程在完成任务后通过 exit() 函数退出,避免成为僵尸进程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值