【其中一些内容发早了,建议阅读本篇前看看下一篇《Linux的错误处理》再阅读本篇】
由于我在Windows下也没怎么用过管道,所以只能照猫画虎来写这个了。
管道是两个进程来进行通信的一种方式,所以被大量采用了。
我给一个输出ls ~输出的例子,因为不太懂管道,所以就是最常用的——捕获另一个程序的输出。
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> int main( ) { FILE *fp; char cmd[] = "ls ~"; char buf[PIPE_BUF]; fp = popen( cmd , "r" ); //pipe open,打开为一个子进程(个人理解),并且是只读模式 if(!fp) //打开失败了 { perror("不能创建管道"); return 1; } while(fgets(buf,sizeof(buf),fp)!=NULL) //抓取管道缓冲区内容 printf("%s",buf); //输出 pclose(fp); //关闭管道 return 0; }
这里创建的是一个匿名管道,只适用于父子、兄弟进程的通信,这里我觉得popen创建的是一个子进程,有错请拍砖。
命名管道适用于任何一个进程间的通信(前提是两个进程都处理了管道代码,否则是无法让进程B接受进程A的信息,即使传送也是没有意义的)
因为LINUX特别的机制(也是UNIX的思想)——一切都是文件。连硬件都是,当然包括管道了。
#include <stdio.h> #include <stdlib.h> #include <memory.h> #include <string.h> //系统调用 #include <errno.h> #include <fcntl.h> #define FIFO_FILE "/tmp/myfifo" #define ARRLEN 8192 void showerror( void ) //云风:只有参数列表为void的函数才是无参 { printf("Error %d: %s\n\n", errno, (char*)strerror(errno)); } int main( ) { int fd; //文件号 char *buff=(char*)malloc(ARRLEN);//8kb的数据 if(buff==NULL) //内存不足 { printf("致命错误:内存不足"); return -1; } memset(buff,0,ARRLEN); //创建管道文件 mkfifo(FIFO_FILE,O_CREAT | O_EXCL); //O_EXCL可执行,O_CREAT无E if (errno!=EEXIST && errno!=0)//不能创建但文件存在 { puts("致命错误:不能创建有名管道"); return 1; } fd=open(FIFO_FILE,O_RDWR|O_TRUNC,0); //读写|清零 if(fd == -1) { showerror(); return 2; } //写入管道 strcpy(buff,"这条信息是使用管道写入的\n" "本管道在/tmp/myfifo\n" "程序结束后会自动删除这个管道\n"); if(write(fd,buff,strlen(buff))==-1) { showerror(); return 3; } //读入管道内容 if(read(fd,buff,ARRLEN)==-1) { showerror(); return 3; } printf("%s",buff); unlink(FIFO_FILE); return 0; }
因为我们对/tmp/没有写入权限,所以你执行必须用root权限才可以。
我试试写一个Server和Client,在Server运行期间,不断抓取管道内容,直到client向里面写入数据,这时输出并退出。
#include <stdio.h> #include <stdlib.h> #include <memory.h> #include <string.h> //系统调用 #include <errno.h> #include <fcntl.h> #define FIFO_FILE "/tmp/myfifo" #define ARRLEN 8192 void showerror( void ) //云风:只有参数列表为void的函数才是无参 { printf("Error %d: %s\n\n", errno, (char*)strerror(errno)); } int main( ) { int fd; //文件号 char *buff=(char*)malloc(ARRLEN);//8kb的数据 if(buff==NULL) //内存不足 { printf("致命错误:内存不足"); return -1; } memset(buff,0,ARRLEN); //创建管道文件 mkfifo(FIFO_FILE,O_CREAT | O_EXCL); //O_EXCL可执行,O_CREAT无E if (errno!=EEXIST && errno!=0)//不能创建但文件存在 { puts("致命错误:不能创建有名管道"); return 1; } fd=open(FIFO_FILE,O_RDWR|O_TRUNC,0); //读写|非阻塞|清零 if(fd == -1) { showerror(); return 2; } //抓取管道内容,每1秒一次 while(1) { if(read(fd,buff,ARRLEN)==-1) { showerror(); return 3; } if(strlen(buff)) break; sleep(1000); } printf("%s",buff); unlink(FIFO_FILE); return 0; }
#include <stdio.h> #include <string.h> #include <fcntl.h> #include <errno.h> #define FIFO_FILE "/tmp/myfifo" void showerror(void) { printf("Error %d: %s\n", errno, strerror(errno)); } int main() { int fd; char buff[] = "这些内容由管道Client写入\n"; fd=open(FIFO_FILE,O_RDWR|O_TRUNC); if(fd == -1) { showerror(); printf("服务端未打开?\n"); return 1; } if(write(fd,buff,strlen(buff))==-1) { showerror(); return -1; } return 0; }
编译,首先执行sudo server,发现没有任何输出,而且迟迟不退出,这正是我们期望的,再打开一个终端GUI,执行sudo client,发现服务端处输出“这些内容由管道Client写入”,并且client和server都退出了,这也是我们期望的。不过我发现,循环内指示一秒抓取一次,说明在执行client时server应有一点延迟,然而我并未感觉到延迟,这可能是1秒太短暂了
正当我打算在server内使用remove删除FIFO_FILE时,意外发生了,/tmp/内并没有myfifo文件,这如何解释?我认为Server可以看作管道的拥有者,当unlink触发时,管道与拥有者断开链接,这时候,unlink就会轻松地将myfifo送上西天。你可能觉得这与mkfifo有关联,认为这会导致管道和程序连理某种不可告人的连接。我也曾经这样想过,不过不幸的是,再看看client,这下就明了了,实际上,它……仅……仅……是……一……个……文……件!!!!!!!或许你会觉得把文件还故弄玄虚叫成管道太麻烦,当然,你也可以用文件来搞定!共享读写就行了。
你又有什么关于Linux管道的看法,告诉我吧!毕竟,Windows的管道生成太麻烦了,竟然要填写什么结构,还要写具有超多参数的CreatePipe(与Windows的窗口类和CreateWindow[Ex]有点类似)