管道和FIFO
1.1 管道和FIFO简介
管道是最初的unixIPC形式,广义的管道包含无名管道(狭义的管道)和有名管道(FIFO)
无名管道采用pipe函数创建,只能由亲缘关系的进程使用;有名管道突破了亲缘关系的限制,可以在不同进程间实现数据共享,管道和FIFO都是使用通常的read和write函数访问的,其由mkfifo函数创建,然后用open函数打开使用。
对管道或FIFO的write总是往末尾添加数据,对它们的read则总是从开头返回数据。这相当于队列。如果对其调用lseek,那就返回ESPIPE错误。
另外标准I/O库提供了popen函数和pclose函数,用于创建一个管道并启动另外一个进程,该进程要么从该管道读出标准输入,要么往该管道写入标准输出。
#include <stdio.h>
FILE *popen(const char *command, const char*type);
int pclose(FILE *stream);
p. s: shell脚本中管道符“|”的实现估计就是利用这个
1.1.1 原理
管道的原理图如下所示:
从原理图中可以看出,父进程和子进程通过管道进行通信,当读写数据时要进行用户态和内核态的穿越(使用read和write进行读写)
1.1.2 程序示例:
1) pipe
下面的程序演示了父子进程间通过pipe进行通信,当输入hello时,子进程传递给父进程,再由父进程返回给子进程输出到标准输出。
server.c#include <unistd.h> #include <stdio.h> #define MAXLINE 100 void server(int readfd, int writefd) { intfd; ssize_tn; charbuff[MAXLINE + 1 ]; if( (n = read(readfd, buff, MAXLINE)) == 0) { fprintf(stderr,"end-of-file while reading pathname"); } buff[n]= 0; //printf("readfrom client, buff = %s, n = %d", buff, n); write(writefd,buff, n); } client.c #include <unistd.h> #include <stdio.h> #define MAXLINE 100 void client(int readfd, int writefd) { size_tlen; intn; charbuff[MAXLINE]; fgets(buff,MAXLINE, stdin); len= strlen(buff); if(' ' == buff[len - 1]) len--; //writepathname to IPC channel write(writefd,buff, len); //readfrom IPC, write to standard output while((n= read(readfd, buff, MAXLINE)) > 0) write(STDOUT_FILENO,buff, n); } mainpipe.c #include "unistd.h" #include "stdio.h" void client(int, int); void server(int, int); int main(int argc, char **argv) { intpipe1[2]; intpipe2[2]; pid_tchildpid; //createtwo pipes pipe(pipe1); pipe(pipe2); if(0 == (childpid = fork())) //child process { close(pipe1[1]);//close the writePort close(pipe2[0]);//close the readPort server(pipe1[0],pipe2[1]); exit(0); } //parentprocess close(pipe1[0]);//close the readPort close(pipe2[1]);//close the writePort client(pipe2[0],pipe1[1]); waitpid(childpid,NULL, 0); exit(0); }
makefile
OBJS=mainpipe.o client.o server.o
mainpipe:${OBJS}
g++-o $@ mainpipe.o client.o server.o
clean:
rm-f mainpipe ${OBJS}
2) FiFO
下面的程序演示了使用FIFO进行两个无亲缘关系进程间通信(其中server.c和client.c同上):
server_main.c#include <sys/types.h> #include <sys/stat.h> #include "fifo.h" #include <stdio.h> #include <fcntl.h> #include <errno.h> void server(int, int); int main(int argc, char **argv) { intreadfd, writefd; if((mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST)) { printf("can't create %s ",FIFO1);} if((mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST)) { unlink(FIFO1); printf("can'tcreate %s ", FIFO2); } readfd= open(FIFO1, O_RDONLY, 0); writefd= open(FIFO2, O_WRONLY, 0); server(readfd,writefd); exit(0); }client_main.c#include "fifo.h" #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char **argv) { intreadfd, writefd; writefd= open(FIFO1, O_WRONLY, 0); readfd= open(FIFO2, O_RDONLY, 0); client(readfd,writefd); close(readfd); close(writefd); exit(0); }
makefile
OBJS=server_main.o client_main.o client.oserver.o
all: server_main client_main
client_main:${OBJS}
g++-o $@ client_main.o client.o server.o
server_main:${OBJS}
g++-o $@ server_main.o client.o server.o
clean:
rm-f server_main client_main ${OBJS}
1.1.3 总结
最后对两者的不同点做一个比较:
1、 适用范围:FIFO即可以用于亲缘关系的进程通信,又可用于无亲缘关系的进程通信
2、 创建函数:创建并打开一个管道只需要调用pipe,创建并打开一个FIFO则需在调用mkfifo之后再调用open
3、 打开管道后的删除方式:管道在所有进程最终都关闭它之后自动消失。FIFO的名字则只有通过调用unlink才从文件系统删除