1、函数列表
<!--[if !supportLists]-->Ø <!--[endif]-->int mkfifo(const char *pathname, mode_t mode);
如果pathname指向的FIFO不存在,则创建FIFO,此时返回0;如果pathname指向的FIFO已经存在,则返回-1,errno==EEXIST。
关于mode,此函数已隐含包含O_CREATE和O_EXCL,也就是说要么创建一个新的FIFO,要么返回EEXIST错误。指定S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH表示FIFO允许用户读、用户写、组成员读和其他用户读。
<!--[if !supportLists]-->Ø <!--[endif]-->int open(const char *pathname, int flags, mode_t mode);
一个FIFO创建完后(或者是已经存在),它必须或者打开用于读,或者打开用于写。对于一个进程而言,FIFO不能打开来既读又写,因为FIFO是半双工的。
<!--[if !supportLists]-->Ø <!--[endif]-->ssize_t write(int fd, const void *buf, size_t count);
<!--[if !supportLists]-->Ø <!--[endif]-->ssize_t read(int fd, void *buf, size_t count);
<!--[if !supportLists]-->Ø <!--[endif]-->int close(int fd); //#include <unistd.h>
操作文件(广义的:包括普通文件、系统设备和管道)的读写关闭函数。其中fd为文件描述字。
<!--[if !supportLists]-->Ø <!--[endif]-->int unlink(const char *pathname);
此函数将FIFO从系统中彻底删除。成功返回0,失败返回-1。
2、实例解析
<!--[if !supportLists]-->Ø <!--[endif]-->Simplefifo
FIFO的基本使用,在父子进程中使用单个FIFO,父进程向FIFO的写文件描述字写进数据,子进程从FIFO的读文件描述字中读出数据。
<!--[if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter" /> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0" /> <v:f eqn="sum @0 1 0" /> <v:f eqn="sum 0 0 @1" /> <v:f eqn="prod @2 1 2" /> <v:f eqn="prod @3 21600 pixelWidth" /> <v:f eqn="prod @3 21600 pixelHeight" /> <v:f eqn="sum @0 0 1" /> <v:f eqn="prod @6 1 2" /> <v:f eqn="prod @7 21600 pixelWidth" /> <v:f eqn="sum @8 21600 0" /> <v:f eqn="prod @7 21600 pixelHeight" /> <v:f eqn="sum @10 21600 0" /> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect" /> <o:lock v:ext="edit" aspectratio="t" /> </v:shapetype><v:shape id="_x0000_i1025" type="#_x0000_t75" style='width:368.25pt; height:202.5pt'> <v:imagedata src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image001.png" o:title="" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->
//simplefifo.c #include <sys/types.h> #include <sys/stat.h> #include <pthread.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> //O_RDONLY #include <errno.h> #define MAX_LENGTH 10 int main(int argc, char **argv) { int readfd, writefd; pid_t childpid; int w_length; int r_length; char buffer[MAX_LENGTH]; if ((mkfifo("/tmp/fifo.1", (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))<0) && (errno == EEXIST)) printf("can't create %s", "/tmp/fifo.1"); if((childpid=fork())==0) //child process server { readfd = open("/tmp/fifo.1", O_RDONLY, 0); r_length=read(readfd,buffer,MAX_LENGTH); printf("r_length===%d/n",r_length); printf("buffer===%s/n",buffer); printf("buffer===%d/n",strlen(buffer)); close(readfd); } else { writefd=open("/tmp/fifo.1", O_WRONLY, 0); w_length=write(writefd,"abcdef",11); printf("w_length===%d/n",w_length); waitpid(childpid, NULL, 0); close(writefd); unlink("/tmp/fifo.1"); } } # gcc simplefifo.c -lpthread -o simplefifo |
<!--[if !supportLists]-->Ø <!--[endif]-->norelationsprocessfifo
没有亲缘关系的进程之间使用一个FIFO。将其中阻塞的进程称为服务端进程,将另一个称为客户端进程。客户端向FIFO写数据,服务端从FIFO读取数据。
<!--[if gte vml 1]><v:shape id="_x0000_i1026" type="#_x0000_t75" style='width:369pt;height:199.5pt'> <v:imagedata src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image003.png" o:title="" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->
// norelationsprocessfifo_server.c #include <sys/types.h> #include <sys/stat.h> //mkfifo #include <errno.h> // errno #include <fcntl.h> //O_RDONLY int main(int argc, char **argv) { int readfd; int r_length; char buffer[10];
if ((mkfifo("/tmp/fifo.1", (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))<0) && (errno == EEXIST)) printf("can't create %s", "/tmp/fifo.1"); printf("befor open fifo/n"); readfd = open("/tmp/fifo.1", O_RDONLY, 0); printf("have open and befor read/n"); r_length=read(readfd,buffer,10); printf("have read and r_length=%d;strlen(buffer)=%d;buffer=%s;/n",r_length,strlen(buffer),buffer); close(readfd); } //#gcc norelationsprocessfifo_server.c –o norelationsprocessfifo_server |
// norelationsprocessfifo_client.c #include <sys/types.h> #include <sys/stat.h> //mkfifo #include <fcntl.h> //O_RDONLY int main(int argc, char **argv) { int writefd; int w_length; printf("befor open fifo/n"); sleep(5); writefd=open("/tmp/fifo.1", O_WRONLY, 0); printf("fifo have open and befor write/n");
sleep(5); w_length=write(writefd,"abcdef",11); printf("have write/n");
sleep(5); close(writefd); unlink("/tmp/fifo.1"); } //#gcc norelationsprocessfifo_client.c –o norelationsprocessfifo_client |
服务端创建FIFO,并阻塞(等待客户端打开FIFO写文件描述字)打开FIFO读文件描述字,然后阻塞(等待客户端向FIFO的写文件描述字写进数据)从FIFO读文件描述字中读取数据,最后关闭读文件描述字。
客户端打开FIFO的写文件描述字,然后向FIFO写文件描述字中写进数据,然后关闭写文件描述字,最后删除FIFO。
通过程序的输出可以很明显的看出阻塞过程,其中方框中输出对应的服务端open和read函数即为阻塞点,示意图如下:
<!--[if gte vml 1]><v:shape id="_x0000_i1027" type="#_x0000_t75" style='width:411.75pt;height:278.25pt'> <v:imagedata src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image005.png" o:title="" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->
阻塞输出示意图
阻塞发生在服务端,因此只需要在服务端以非阻塞方式(O_NONBLOCK)打开FIFO的读文件描述字,则open函数和read函数均以非阻塞方式执行。
//norelationsprocessfifo_noblock_server.c #include <sys/types.h> #include <sys/stat.h> //mkfifo #include <errno.h> // errno #include <fcntl.h> //O_RDONLY int main(int argc, char **argv) { int readfd; int r_length; char buffer[10];
if ((mkfifo("/tmp/fifo.1", (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))<0) && (errno != EEXIST)) printf("can't create %s", "/tmp/fifo.1");
printf("befor open fifo/n"); readfd = open("/tmp/fifo.1", O_RDONLY|O_NONBLOCK, 0); printf("have open and befor read/n");
int p=0; while(1) { r_length=read(readfd,buffer,10); printf("%d/n",p++); if(r_length>0) { printf("have read and r_length=%d;strlen(buffer)=%d;buffer=%s;/n",r_length,strlen(buffer),buffer); sleep(3); } } close(readfd); } //#gcc norelationsprocessfifo_noblock_server.c –o noblockserver |
由于read函数也不发生阻塞,因此必须有一个无限循环等待客户端发送数据。
norelationsprocessfifo_noblock_client.c代码和norelationsprocessfifo_client.c相同。
<!--[if !supportLists]-->Ø <!--[endif]-->structmesg
进程之间可以通过FIFO传递struct变量数据。
//structmesg.h #include <sys/types.h> #include <sys/stat.h> //mkfifo #include <fcntl.h> //O_RDONLY #include <limits.h> /* PIPE_BUF */
#define MAXMESGDATA (PIPE_BUF - 2*sizeof(long)) #define MESGHDRSIZE (sizeof(struct mymesg) - MAXMESGDATA)
struct mymesg { long mesg_len; /* #bytes in mesg_data, can be 0 */ long mesg_type; /* message type, must be > 0 */ char mesg_data[MAXMESGDATA]; }; |
//structmesg_server.c #include <errno.h> #include "structmesg.h" int main(int argc, char **argv) { int readfd; size_t len; ssize_t n; struct mymesg mesg; if ((mkfifo("/tmp/fifo.1", (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))<0) && (errno == EEXIST)) printf("can't create %s", "/tmp/fifo.1");
readfd = open("/tmp/fifo.1", O_RDONLY, 0); if ( (n = read(readfd, &mesg, MESGHDRSIZE)) == 0) //第一次读取固定长度数据,得到数据类型和结构体数据长度 { printf("end of file!/n"); //到达文件的末尾,没有数据 return(0); /* end of file */ } else if (n != MESGHDRSIZE) { printf("message header: expected %d, got %d", MESGHDRSIZE, n); //数据有误,长度不一致 return(0); }
if ( (len = mesg.mesg_len) > 0)//结构体中有具体的数据,长度不为0 if ( (n = read(readfd, mesg.mesg_data, len)) != len) //第二次读取具体的结构体数据 { printf("message data: expected %d, got %d", len, n);//数据有误,长度不一致 return(0); }
printf("mesg_len=%d/tmesg_type=%d/tmesg_data=%s/n",mesg.mesg_len,mesg.mesg_type,mesg.mesg_data); close(readfd); return 0; } |
注意判断各种可能的情况。第一次读取结构体数据长度和类型是用struct mymesg指针作为入参,那么只填充了前面两个成员变量,这可以从struct的存储结构来理解;第二次读取是以struct mymesg的成员mesg_data作为入参。
#include "structmesg.h" int main(int argc, char **argv) { int writefd; struct mymesg mesg; writefd = open("/tmp/fifo.1", O_WRONLY, 0); mesg.mesg_len=10; mesg.mesg_type=1; strcpy(mesg.mesg_data,"abcdefghij");
write(writefd,&mesg,MESGHDRSIZE+mesg.mesg_len); close(writefd); unlink("/tmp/fifo.1"); } |
3、小结
<!--[if !supportLists]-->Ø <!--[endif]-->pipe(管道)没有名称,因此只能在父子进程之间使用(不考虑传递文件描述字的情况);
而FIFO(具名管道)有名称,因此可以在非亲缘关系进程之间使用。
<!--[if !supportLists]-->Ø <!--[endif]-->FIFO是一种只能在单台主机上使用的IPC(进程间通信)形式。尽管在文件系统中有名
字,它们也只能用在本地文件系统中,而不能用在通过NFS安装的文件系统中。
<!--[if !supportLists]-->Ø <!--[endif]-->创建pipe和获得管道的文件描述字只需要pipe(int fd[2])函数,而mkfifo创建FIFO,open
获得文件描述字。
关闭pipe的文件描述字以后,pipe就自动消失了,而对于FIFO,不仅要关闭其文件描述字,还要调用unlink函数来彻底删除。
<!--[if !supportLists]-->Ø <!--[endif]-->任意时刻打开管道和FIFO描述字的数目是有一定限制的;
管道和FIFO的write操作的原子性的前提是数据量小于PIPE_BUF,在一次写操作过程中数据量小于PIPE_BUF时,写操作原子性能够保证;
数据是面向字节流的,也就是从管道/FIFO一端读出的100个字节,另一端可能1次write100个字节,可能2次50个字节,可能5次20个字节。也就是无法区别对端一次性write的数据量。
针对管道/FIFO数据字节流的特点,在两端必须由应用自定义解释,也就是编制数据传输的协议。
<!--[if !supportLists]-->Ø <!--[endif]-->FIFO和pipe一样可以以全双工的形式来使用,原理也是一个进程同时打开读和写的文
件描述字,但全双工使用较少,尽量采用半双工形式。