进程间通信之FIFO

管道使用起来很方便,但是没有名字,因此只能用于具有亲缘关系的进程之间进行通信,而有名管道就克服了这一点,FIFO管道提供了一个路径名与之相对应,即使进程不是亲缘进程,只要能访问到该路径就能使用FIFO进行通信。

有名管道的创建:

  1. #include <sys/types.h>

  2. #include <sys/stat.h>

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

第一个参数是创建的FIFO的路径名字,如果存在该路径,那么会返回EEXIT错误。

第二个参数和打开普通文件的open函数的mode参数一样。

 

一般文件的IO函数都可以用于FIFO,有名管道和管道相比,其多了一个打开的操作,使用open函数打开已经创建的FIFO。

 

如果当前操作是为读而打开FIFO,那么必须保证此时有一个进程为写而打开改FIFO,否则会阻塞的等待有一个进程为写而打开该FIFO。

如果当前操作是为写而打开FIFO,那么必须保证此时有一个进程为读而打开该FIFO,否则会阻塞的等待有一个进程为读而打开该FIFO。

 

从FIFO中读数据:

约定:

    如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标记的读操作。

    如果没有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标记的读操作来说,将一直阻塞。对于没有设置阻塞标记读操作来说则返回-1,当前errno值为EAGAIN,提醒以后操作。

    对于设置了阻塞标记的读操作来说,造成阻塞的原因有两种:当前FIFO内没有数据,但是其他进程在读这些数据;另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论写入数据的大小,也不论读操作请求多少数据量。

    读打开的阻塞标记只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其他将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样,此时读操作返回0.

    如果没有进程写打开FIFO,则设置了阻塞标记的读操作会一直阻塞。

    注:如果FIFO中有数据,则设置了阻塞标记的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作返回FIFO中实际拥有的数据。

向FIFO中写入数据:

约定:

    如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标记的写操作。

    对于设置了阻塞标记的写操作:

    当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性,如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。

    当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性,FIFO缓冲区以有空闲区域,写进程就会试图向FIFO写数据,写操作在写完所有请求写的数据后返回。

    对于没有设置阻塞标记的写操作:

    当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性,在写满所有FIFO空闲缓冲区后,写操作返回。

    当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳的请求写入的字节数,写完后返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN,提醒以后再写。

 

下面是创建FIFO、向FIFO中写数据、从FIFO中读数据的实例:

创建FIFO:

    

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

 

int main(int argc, char **argv)

{

  mode_t mode = 0666;

 

  if (argc != 2)

  {

    printf("Usages:create_fifo <fifoname>\n");

    _exit(1);

  }

 

  if ((mkfifo(argv[1], mode)) < 0)

  {

    printf("failed to mkfifo!\n");

    _exit(1);

  }

  else

  {

    printf("you successfully create a FIFO name is: %s\n",argv[1]);

    _exit(1);

  }

  return 0;

}


    

向FIFO写数据:

    

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <limits.h>

#include <fcntl.h>

#include <time.h>

 

#define BUFSZ PIPE_BUF

 

int main(void)

{

  int fd = 0;

  int n = 0;

  int i = 0;

  char buf[BUFSZ];

  time_t tp;

 

  printf("i am %d\n",getpid());

 

  if ((fd = open("fifo1", O_WRONLY)) < 0)

  {

    printf("open fifo failed!\n");

    _exit(1);

  }

 

  for(i=0; i<10; i++)

  {

    time(&tp);

 

    n = sprintf(buf, "write_fifo %d sneds %s", getpid(), ctime(&tp));

    printf("send msg :%s\n", buf);

    if((write(fd, buf, n+1)) < 0)

    {

      printf("write failed!\n");

      close(fd);

      _exit(1);

    }

    usleep(3000*1000);

  }

  close(fd);

  return 1;

}


写数据程序编译后运行,由于设置阻塞标记的写操作,所以,在没有以读操作而打开FIFO的进程时,写操作一直阻塞,直到有以读操作而打开的FIFO的进程。

 

从FIFO中读数据:

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <limits.h>

 

#define BUFSZ PIPE_BUF

 

int main(void)

{

  int fd = 0;

  int len = 0;

  char buf[BUFSZ];

 

  mode_t mode = 0666;

 

  if ((fd = open("fifo1", O_RDONLY)) < 0)

  {

    printf("open fifo failed!\n");

    _exit(1);

  }  

 

  while((len = read(fd, buf, BUFSZ)) > 0)

  {

    printf("read_fifo read:%s", buf);

  }

 

  close(fd);

  exit(0);

}


 

当有读操作打开FIFO进程时,写操作进程开始往FIFO中写数据,读进程开始读数据。

你可能感兴趣的:(c,linux)