管道和有名管道是最早的进程间通信机制之一,管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道; 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程); 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系 统,并且只存在与内存中。 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。 #include <unistd.h> int pipe(int fd[2]) 成功返回0,失败返回-1 管道在程序中用一对文件描述符表示,其中一个文件描述符有可读属性,一个有可写的属性。fds[0]是读,fds[1]是写。 像文件操作有标准io流一样,管道也支持文件流模式。用来创建连接到另一进程的管道,是通过函数popen和pclose。 函数原型: #include <stdio.h> FILE* popen(const char* command, const char* open_mode); int pclose(FILE* fp); 函数popen():允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串是要运行的程序名。open_mode必须是“r”或“w”。如果open_mode是“r”,被调用程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE*文件流指针,就可以通过常用的stdio库函数(如fread)来读取被调用程序的输出;如果open_mode是“w”,调用程序就可以用fwrite向被调用程序发送数据,而被调用程序可以在自己的标准输入上读取这些数据。 函数pclose():用popen启动的进程结束时,我们可以用pclose函数关闭与之关联的文件流。 像文件操作有标准io流一样,管道也支持文件流模式。用来创建连接到另一进程的管道,是通过函数popen和pclose。 函数原型: #include <stdio.h> FILE* popen(const char* command, const char* open_mode); int pclose(FILE* fp); 函数popen():允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串是要运行的程序名。open_mode必须是“r”或“w”。如果open_mode是“r”,被调用程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE*文件流指针,就可以通过常用的stdio库函数(如fread)来读取被调用程序的输出;如果open_mode是“w”,调用程序就可以用fwrite向被调用程序发送数据,而被调用程序可以在自己的标准输入上读取这些数据。 函数pclose():用popen启动的进程结束时,我们可以用pclose函数关闭与之关联的文件流。 fdopen函数 #include<stdio.h> FILE * fdopen(int fildes,const char * mode); fdopen取一个现存的文件描述符(我 们可能从 o p e n , d u p , d u p 2 , f c n t l或p i p e函数得到此文件描述符) ,并使一个标准的I / O流与该描述符相结合。此函数常用于由创建管道和网络通信通道函数获得的描述符。因为这些特殊类型的文件不能用标准I/O fopen函数打开,首先必须先调用设备专用函数以获得一个文件描述符,然后用f d o p e n使一个标准I / O流与该描述符相结合。 fdopen()会将参数fildes 的文件描述符,转换为对应的文件指针后返回。参数mode 字符串 则代表着文件指针的流形态,此形态必须和原先文件描述词读写模式相同。
下面是一个使用管道实现的简单的通信。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
char buf[1024];
int fds[2],fds2[2]; //fds[0]用于读 fds[1]用于写
pipe(fds);
pipe(fds2);
if(fork()>0)//父亲进程
{
close(fds[1]);//读的时候应先关闭写
close(fds2[0]); //写的时候先关闭读
while(memset(buf,0,1024),read(fds[0],buf,1024)>0)//从管道读
{
write(1,buf,strlen(buf)); //read是一个阻塞函数,会一直停在这里
}
close(fds[2]); //要使用close关闭管道 管道也会堵塞
printf("father speaking:\n");
FILE *fs=fdopen(fds2[1],"w");//管道与文件指针建立连接 可以不实用文件指针直接对管道操作
if(fs==NULL) //while(memset(buf,0,1024),read(0,buf,1024)>0)
{ //{ write(fds[1],buf,strlen(buf));}
perror("father died!\n");
}
while(memset(buf,0,1024),fgets(buf,1024,stdin)!=NULL)//输入buf中
{
fprintf(fs,"father message:%s",buf);//写入文件
fflush(fs);
}
close(fds2[1]);
wait(NULL);
}
else
{
close(fds[0]);
close(fds2[1]);//关闭写
FILE *fd=fdopen(fds[1],"w");//管道当作一个文件
if(fd==NULL)
{
perror("fdopen wrong\n");
}
while(memset(buf,0,1024),fgets(buf,1024,stdin)!=NULL)
{
fprintf(fd,"child message:%s",buf);
fflush(fd);
}
close(fds[1]);
while(memset(buf,0,1024),read(fds2[0],buf,1024)>0)
{
write(1,buf,strlen(buf));
}
close(fds2[0]);
exit(1);
}
return 0;
}