IPC FIFO

FIFO

1、函数列表

Ø        int mkfifo(const char *pathname, mode_t mode);

如果pathname指向的FIFO不存在,则创建FIFO,此时返回0;如果pathname指向的FIFO已经存在,则返回-1errno==EEXIST

关于mode,此函数已隐含包含O_CREATEO_EXCL,也就是说要么创建一个新的FIFO,要么返回EEXIST错误。指定S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH表示FIFO允许用户读、用户写、组成员读和其他用户读。

Ø        int open(const char *pathname, int flags, mode_t mode);

一个FIFO创建完后(或者是已经存在),它必须或者打开用于读,或者打开用于写。对于一个进程而言,FIFO不能打开来既读又写,因为FIFO是半双工的。

Ø        ssize_t write(int fd, const void *buf, size_t count);

Ø        ssize_t read(int fd, void *buf, size_t count);

Ø        int close(int fd); //#include <unistd.h>

操作文件(广义的:包括普通文件、系统设备和管道)的读写关闭函数。其中fd为文件描述字。

Ø        int unlink(const char *pathname);

此函数将FIFO从系统中彻底删除。成功返回0,失败返回-1

 

2、实例解析

Ø         Simplefifo

FIFO的基本使用,在父子进程中使用单个FIFO,父进程向FIFO的写文件描述字写进数据,子进程从FIFO的读文件描述字中读出数据。

//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

 

Ø         norelationsprocessfifo

没有亲缘关系的进程之间使用一个FIFO。将其中阻塞的进程称为服务端进程,将另一个称为客户端进程。客户端向FIFO写数据,服务端从FIFO读取数据。

//  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

通过程序的输出可以很明显的看出阻塞过程,其中方框中输出对应的服务端openread函数即为阻塞点,示意图如下:

阻塞输出示意图

阻塞发生在服务端,因此只需要在服务端以非阻塞方式(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相同。

Ø         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、小结

Ø         pipe(管道)没有名称,因此只能在父子进程之间使用(不考虑传递文件描述字的情况);

FIFO(具名管道)有名称,因此可以在非亲缘关系进程之间使用。

Ø         FIFO是一种只能在单台主机上使用的IPC(进程间通信)形式。尽管在文件系统中有名

字,它们也只能用在本地文件系统中,而不能用在通过NFS安装的文件系统中。

Ø         创建pipe和获得管道的文件描述字只需要pipe(int fd[2])函数,而mkfifo创建FIFOopen

获得文件描述字。

关闭pipe的文件描述字以后,pipe就自动消失了,而对于FIFO,不仅要关闭其文件描述字,还要调用unlink函数来彻底删除。

Ø         任意时刻打开管道和FIFO描述字的数目是有一定限制的;

管道和FIFOwrite操作的原子性的前提是数据量小于PIPE_BUF,在一次写操作过程中数据量小于PIPE_BUF时,写操作原子性能够保证;

数据是面向字节流的,也就是从管道/FIFO一端读出的100个字节,另一端可能1write100个字节,可能250个字节,可能520个字节。也就是无法区别对端一次性write的数据量。

针对管道/FIFO数据字节流的特点,在两端必须由应用自定义解释,也就是编制数据传输的协议。

Ø         FIFOpipe一样可以以全双工的形式来使用,原理也是一个进程同时打开读和写的文

件描述字,但全双工使用较少,尽量采用半双工形式。

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值