实验三 进程间通信实验
一、实验目的
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秒钟后向子进程发送信号10(SIGUSR1)。子进程在收到信号后,将k设置为0,进入循环等待k为0,并在k为0时输出"Child process is killed by parent!"。父进程在发送信号后,等待子进程结束,并在子进程结束后输出"Parent process has killed!"。
- #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");
}
编译及执行过程和运行结果截屏:
解释程序实现的功能:
程序中定义了一个全局变量k,并将其初始值设置为1。然后通过signal函数将SIGINT信号(键盘中断信号)与一个自定义的信号处理函数func关联起来。在信号处理函数func中,将全局变量k的值设置为0,表示程序需要结束。在主函数中,通过一个while循环来监测k的值,如果k的值不为0,则一直循环。当用户在键盘上按下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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
pid_t child_process1;
pid_t child_process2;
void signal_handler(int signum) {
kill(child_process1, SIGINT);
kill(child_process2, SIGINT);
}
void child1_signal_handler(int signum) {
printf("Child process 1 is killed by parent!\n");
exit(0);
}
void child2_signal_handler(int signum) {
printf("Child process 2 is killed by parent!\n");
exit(0);
}
int main() {
child_process1 = fork();
if (child_process1 == 0) {
// Child process 1
signal(SIGINT, child1_signal_handler);
while(1);
}
child_process2 = fork();
if (child_process2 == 0) {
// Child process 2
signal(SIGINT, child2_signal_handler);
while(1);
}
if (child_process1 > 0 && child_process2 > 0) {
// Parent process
signal(SIGINT, signal_handler);
wait(NULL);
wait(NULL);
printf("Parent process is killed!\n");
exit(0);
}
return 0;
}
编译及执行过程和运行结果截屏:
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;
}
int 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");
// 取得司机进程的识别码
pid_t driver_pid = getppid();
// 发送信号给司机
kill(driver_pid, 12);
count--;
}
}
return 0;
}编译及执行过程和结果截屏:
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);
}
}
}
编译及执行过程和运行结果截屏。
5、在父进程中用pipe()建立一条管道线,往管道里写信息,两个子进程接收父进程发送的信息。(2分)
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define BUFFER_SIZE 100
int main() {
int fd[2]; // 创建一个包含两个文件描述符的数组,用于存放管道的读端和写端
pid_t pid1, pid2; // 用于存放子进程的进程ID
char buffer[BUFFER_SIZE];
// 创建管道
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
// 创建第一个子进程
pid1 = fork();
if (pid1 < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid1 == 0) {
// 子进程1从管道中读取信息,并输出到屏幕上
close(fd[1]); // 关闭写端
read(fd[0], buffer, BUFFER_SIZE);
printf("Child Process 1 received message: %s\n", buffer);
close(fd[0]); // 关闭读端
exit(0);
} else {
// 父进程继续执行创建第二个子进程
pid2 = fork();
if (pid2 < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid2 == 0) {
// 子进程2从管道中读取信息,并输出到屏幕上
close(fd[1]); // 关闭写端
read(fd[0], buffer, BUFFER_SIZE);
printf("Child Process 2 received message: %s\n", buffer);
close(fd[0]); // 关闭读端
exit(0);
} else {
// 父进程往管道中写入信息
close(fd[0]); // 关闭读端
char message[] = "Hello from Parent Process!";
write(fd[1], message, strlen(message) + 1);
close(fd[1]); // 关闭写端
wait(NULL); // 等待第一个子进程结束
wait(NULL); // 等待第二个子进程结束
}
}
return 0;
}
编译及执行过程和运行结果截屏。
6、编程用管道实现父子进程间的双向通信。要求给出设计思路,调试通过的程序和编译执行过程以及结果截屏。(附加题)
设计思路:
- 使用pipe()函数在父进程和子进程之间创建两条管道线,一条用于父进程向子进程传递信息,另一条用于子进程向父进程传递信息。
- 父进程创建两个子进程,并分别在每个子进程中使用不同的文件描述符来读取和写入信息。
- 父进程通过管道向子进程发送信息,子进程通过管道向父进程发送信息。
代码:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define BUFFER_SIZE 100
int main() {
int fd1[2], fd2[2]; // 创建两个管道
pid_t pid1, pid2; // 用于存放子进程的进程ID
char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
// 创建第一个管道
if (pipe(fd1) == -1) {
fprintf(stderr, "Pipe1 failed");
return 1;
}
// 创建第二个管道
if (pipe(fd2) == -1) {
fprintf(stderr, "Pipe2 failed");
return 1;
}
// 创建第一个子进程
pid1 = fork();
if (pid1 < 0) {
fprintf(stderr, "Fork1 failed");
return 1;
} else if (pid1 == 0) {
// 子进程1从管道1中读取信息,并输出到屏幕上
close(fd1[1]); // 关闭管道1写端
read(fd1[0], buffer1, BUFFER_SIZE);
printf("Child Process 1 received message: %s\n", buffer1);
// 子进程1向管道2写入信息
close(fd2[0]); // 关闭管道2读端
char message1[] = "Message from Child Process 1";
write(fd2[1], message1, strlen(message1) + 1);
close(fd1[0]); // 关闭管道1读端
close(fd2[1]); // 关闭管道2写端
exit(0);
} else {
// 创建第二个子进程
pid2 = fork();
if (pid2 < 0) {
fprintf(stderr, "Fork2 failed");
return 1;
} else if (pid2 == 0) {
// 子进程2从管道2中读取信息,并输出到屏幕上
close(fd2[1]); // 关闭管道2写端
read(fd2[0], buffer2, BUFFER_SIZE);
printf("Child Process 2 received message: %s\n", buffer2);
// 子进程2向管道1写入信息
close(fd1[0]); // 关闭管道1读端
char message2[] = "Message from Child Process 2";
write(fd1[1], message2, strlen(message2) + 1);
close(fd2[0]); // 关闭管道2读端
close(fd1[1]); // 关闭管道1写端
exit(0);
} else {
// 父进程向管道1写入信息
close(fd1[0]); // 关闭管道1读端
char message[] = "Message from Parent Process";
write(fd1[1], message, strlen(message) + 1);
// 父进程从管道2中读取信息,并输出到屏幕上
close(fd2[1]); // 关闭管道2写端
read(fd2[0], buffer2, BUFFER_SIZE);
printf("Parent Process received message: %s\n", buffer2);
close(fd1[1]); // 关闭管道1写端
close(fd2[0]); // 关闭管道2读端
wait(NULL); // 等待子进程1结束
wait(NULL); // 等待子进程2结束
}
}
return 0;
}
编译及执行过程和运行结果截屏。
三、实验总结和体会(1分)
在进行操作系统进程间通信的实验中,我通过使用管道来实现父进程和子进程之间的双向通信。这个实验让我更深入地理解了进程间通信的概念和原理。
通过使用管道,我实现了父进程向子进程发送信息,并且子进程可以接收到并处理这些信息。同时,子进程也能向父进程发送信息,父进程能够接收并处理这些信息。这种双向通信的方式非常实用,可以在多个进程之间实现数据的传递和共享。
在实验过程中,我注意到了管道的读写顺序。如果读写的顺序不一致,会导致阻塞并无法正常进行通信。此外,我还发现了管道在父子进程之间的传递性,即父进程可以通过管道向子进程传递信息,子进程也可以通过管道向父进程传递信息。
通过这个实验,我不仅加深了对进程间通信的理解,还学会了如何使用管道来实现进程间的双向通信。这将对我未来的学习和工作有很大的帮助。同时也让我更加深入地理解了操作系统的原理和机制。