一、简介
所谓管道,是指用于连接一个读进程和一个写进程,以实现它们之间通信的共享文件,又称 pipe 文件。
向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入管道;而接收管道输出的接收进程(即读进程),可从管道中接收数据。由于发送进程和接收进程是利用管道进行通信的,故又称管道通信。
为了协调双方的通信,管道通信机制必须提供以下3 方面的协调能力。
(1)互斥。当一个进程正在对 pipe 进行读/写操作时,另一个进程必须等待。
(2)同步。当写(输入)进程把一定数量(如4KB)数据写入 pipe 后,便去睡眠等待,直到读(输出)进程取走数据后,再把它唤醒。当读进程读到一空 pipe 时,也应睡眠等待,直至写进程将数据写入管道后,才将它唤醒。
(3)对方是否存在。只有确定对方已存在时,才能进行通信。
二、环境搭建
本次测试pipe通信的应用例程是运行在ubuntu pc上的;当然也是可以运行在linux开发板 或相关linux设备上的。
开发环境无特别要求。
三、例程代码
本次代码会使用单独的一个c文件用来pipe进程代码,使用子进程写入数据到Pipe中,并通过主进程读取pipe中的数据并打印。
创建.c文件,并输入一下内容:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#define INPUT 1
#define OUTPUT 0
void main()
{
int Upipe[2]; //用以管道描述符
pid_t pid; //用于子进程pid创建
char buf[256]; //用于存放接收管道的数据
int returned_count; //读取管道的返回值状态
int i =0; //计数用
char Sendbuf[256]; //用于存放发送管道的数据
/*创建无名管道*/
//pipe(Upipe);
if (pipe(Upipe) == -1) { //创建管道
perror("pipe"); //终端输出
exit(EXIT_FAILURE); //退出
}
/*创建子进程*/
if((pid = fork()) == -1){ //创建子进程
printf("Error in fork\n"); //终端输出
exit(1); //退出
}
if(pid == 0) {
while(1){ /*子进程运行内容*/
printf("in the spawned child process ... \n");
sprintf(Sendbuf,"TEST data:%d \n",i); //组合需要发送的数据内容
if(i%2) //计数值对2取余数
{
close(Upipe[OUTPUT]); //管壁输入
write(Upipe[INPUT],Sendbuf, strlen(Sendbuf));
}
i++;
if(i>100000)
{
i=0;
}
sleep(1);
}
} else {
while(1){ /*主进程运行内容*/
printf("in the spawned parent process ... \n");
close(Upipe[INPUT]);
returned_count = read(Upipe[OUTPUT],buf, sizeof(buf));
printf("%d bytes of data received from spawned prcess :%s \n",returned_count, buf);
sleep(1);
}
}
}
四、编译运行
1、编译命令:
gcc -g -o UnnamedPipes UnnamedPipes.c
2、运行命令
./UnnamedPipes
3、运行输出效果如下图:
五、总结
管道写操作流程如下:
写之前先锁定内存。
获取可写的缓冲区,若可写则循环写。
若不可写,则唤醒读进程,同时写进程进行休眠,让出 CPU。
读的操作很简单,操作如下:
读之前先锁定内存。
从缓冲区中获取数据页,把页中数据拷贝到用户缓冲区中。
数据全部被读完,则发送信号唤醒写进程,同时读进程让出 CPU 进行休眠。
管道也称无名管道,是 UNIX 系统中进程间通信(IPC)中的一种。
管道由于是无名管道,因此只能在有亲缘关系的进程间使用。
管道不是普通的文件,它是基于内存的。
管道属于半双工,数据只能从一方流向另一方,也即数据只能从一端写,从另一端读。
管道中读数据是一次性的操作,数据读取后就会释放空间,让出空间供更多的数据写。
管道写数据遵循先入先出的原则。
注意,在这个例子中,为什么这两个进程都关闭它所不需的管道端呢?
这是因为写进程完全关闭管道端时,文件结束的条件被正确地传递给读进程。而读进程完全关闭管道端时,写进程无需等待继续写数据。
阻塞读和写分别成为对空和满管道的默认操作,这些默认操作也可以改变,这就需要调用 fcntl() 系统调用,对管道文件描述符设置 O_NONBLOCK 标志可以忽略默认操作:
#include <fcntl.h>
fcntl(fd,F_SETFL,O_NONBlOCK);
管道作为进程通信的一种方法,在实际开发中也是经常会使用的。上述例程是一个简单的运用例子。读者自行参考学习使用。
欢迎交流…