管道和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('\n' == 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\n",FIFO1);}
if((mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST))
{
unlink(FIFO1);
printf("can'tcreate %s\n", 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才从文件系统删除