OS实验-管道通信(含代码实现)
管道通信相关概念
所谓“管道”,是指用于连接一个读进程和一个写进程以实现他们之间通信的一个共享文件,又名pipe文件。其中,向管道(共享文件)提供输入的发送进程(即写进程), 以字符流形式将大量的数据送入管道;而接受管道输出的接收进程(即读进程),则从管道中接收(读)数据。由于发送进程和接收进程是利用管道进行通信的,故又称为管道通信。
管道通信首创于UNIX系统,由于它能有效地传送大量数据,因而又被引入到许多其它操作系统中。
为实现管道通信,需要管道机制提供以下三方面协调的能力:
-
互斥
-
同步
-
确定对方是否存在
实验内容
I. 父进程首先使用系统调用 ( 系统调用是干嘛的?-- 作用:把应用程序的请求传给内核,调用相应的内核函数完成所需的处理,再将处理结果返回给应用程序 ) pipe() 来建立一个管道,然后使用系统调用 fork() 来创建子进程1(2);子进程1(2)关闭管道读文件;子进程1(2)通过文件I/O操作向管道写文件写任意一句话(向文件中写入字符串);然后子进程1调用 exit() 结束运行。
II. 父进程通过文件I/O操作从管道读文件中读出来自于两个子进程的信息,通过printf语句打印输出在屏幕上。
程序流程图如下(截取自ppt)
相关函数的使用——均声明在unistd.h头文件中
函数 | 函数原型 | 作用 | 返回值 |
---|---|---|---|
fork | pid_t fork(void) | 创建一个子进程 | 若返回值 > 0, 则说明为父进程;若返回值= 0, 则说明为子进程;若返回值 < 0, 则说明创建进程出错 |
实例:
for(i=0; i<5; i++){ // 实现5次循环,即总共创建 2^5 = 32 个进程(子进程也会创建它的子进程);
if(!(child = fork()))
{
printf("pid %d: %d is my father\n",getpid(),getppid());
sleep(1);
}
else
{
int myself = getpid();
printf("pid %d: %d is my son\n",myself, child);
sleep(1);
}
}
注意:不同于线程,进程之间的资源不可共享;在执行过程中,多个进程的起始信息为其父进程信息的复制版本。
函数 | 函数原型 | 作用 |
---|---|---|
pipe | int pipe(int fildes[2]); | 创建一个通信缓冲区,通过fildes[i]来访问缓冲区,i=0为读打开,i=1为写打开 |
read | ssize_t read( int filedes, void *buf, size_t nbytes); | 从打开的管道文件当中去读取数据 |
write | ssize_t write( int filedes, const void *buf, size_t nbytes); | 从打开的管道文件当中去写数据 |
实例:
// 此代码来自实验ppt
char bufin[BUFSIZE] = "empty";
char bufout[] = "hello";
int bytesin;
pid_t childpid;
int fd[2];
if (pipe(fd) == -1) {
perror("Failed to create the pipe");
return 1;
}
bytesin = strlen(bufin);
childpid = fork();
if (childpid == -1) {
perror("Failed to fork");
return 1;
}
if (childpid)
write(fd[1], bufout, strlen(bufout)+1);
else{
bytesin = read(fd[0], bufin, BUFSIZE);
fprintf(stderr, "[%ld]:my bufin is {%.*s}, my bufout is {%s}\n",(long)getpid(), bytesin, bufin, bufout);
return 0;
}
此处解释以下上述代码中的 {%.*s}, 其在后跟了两个值,分别是bytesin与bufin,意义在于输出的bufin的长度是不能超过bytesin的。
具体代码实现
/**
************************************************************
************************************************************
* 作者: 曾彬芮
*
* 日期: 2021-04-11
*
* 版本: V2.0
*
* 说明: 管道通信的实现
************************************************************
************************************************************
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAXSIZE 100
int main()
{
pid_t Child_ID;
char bufin1[MAXSIZE]="Hello"; //输入的字符串,也可用strcpy(bufin,“Hello”)进行赋值
char bufin2[MAXSIZE]="World";
int Byte_Len1, Byte_Len2;
int filedes[2]; //定义缓冲区
if(pipe(filedes)==-1){ //若缓冲区创建失败
perror("Error in creating the pipe\n");
return 1;
}
Child_ID = fork(); //创建第一个子进程
if(Child_ID==0){
lockf(filedes[1],1,0); //上锁
//strcpy(bufin1,"Hello");
write(filedes[1],bufin1,strlen(bufin1)+1); //写入字符串
fprintf(stderr,"I am process :[%d] and I put {%s} in\n",getpid(),bufin1);
lockf(filedes[1],0,0); //解锁
exit(0); //第一个子进程成功退出
}
Child_ID = fork(); //创建第二个子进程
if(Child_ID==0){
sleep(1); //等待第一个子进程结束
lockf(filedes[1],1,0);
//strcpy(bufin2,"World");
write(filedes[1],bufin2,strlen(bufin2)+1); //strlen(bufin2)+1表示长度
fprintf(stderr,"I am process :[%d] and I put {%s} in\n",getpid(),bufin2);
lockf(filedes[1],0,0);
exit(0);
}
sleep(2); //等待两个进程成功退出
Byte_Len1 = read(filedes[0],bufin1,strlen(bufin1)+1);
Byte_Len2 = read(filedes[0],bufin2,strlen(bufin2)+1);
fprintf(stderr,"I am father [%d] process and my bufout is{%.*s}{%.*s}\n",getpid(),Byte_Len1,bufin1,Byte_Len2,bufin2);
return 0;
}
代码完成后在Linux终端使用命令 gcc -o ### ###.c 编译,再输入 ./### 运行即可(###表示文件名)。
运行结果如下:
以上代码均为自己编写,并已在Linux虚拟机下成功编译运行,若仍有问题还劳烦各位读者朋友指正!
2021.4.13 于 电子科技大学(沙河校区)