管道与命名管道(FIFO文件)

管道是内核中的一个单向的数据通道,同时也是一个数据队列。具有一个读取端与一个写入端,
每一端对应着一个文件描述符。通过使用pipe系统调用创建管道。当父进程调用fork时,所有的
连接、文件描述符都会从父进程传递给子进程。只有有共同父进程的进程之间才可以用管道连接

管道类似于文件,但又与文件不同。
    相同: 都是不带有任何结构的字节序列
    不同:

从管道总读取数

    管道读取阻塞:
                   当进程试图从管道中读取数据时,进程被挂起直到数据被写进管道。

    管道的读取结束标志:
                   当所有的写者关闭了管道的写数据端时,试图从管道读取数据的调用
                   返回0,者意味着文件的结束。

    多个读者可能会引起麻烦:
                   管道是一个队列。当进程从管道总读取数据之后,数据已经不存在了。
                   当两个进程同时对一个管道进行读取时,要注意数据的完整性。
                    

向管道中写数据

    写入的数据阻塞直到管道有空间去容纳新的数据。
    若无读者在读取数据,则写操作失败。

 

命名管道 即FIFO文件,通过命名管道可以在不相关的进程之间交换数据。FIFO有路径名与之相关联,以一种特殊设备文件形式存在于文件系统中

FIFO有两种用途:

    (1)FIFO由shell使用以便数据从一条管道线传输到另一条,为此无需创建临时文件。
    (2)FIFO用于客户进程-服务器进程程序中,已在客户进程与服务器进程之间传送数据。

FIFO的读写规则:

从FIFO中读取数据

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

2.对于设置了阻塞标志的读操作来说,造成阻塞的原因有两种:一种是当前FIFO内有数据,但有其它进程
  再读这些数据;另一种是FIFO内没有数据,阻塞原因是FIFO中有新的数据写入,而不论新写入数据量的
  大小,也不论读操作请求多少数据量。

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

4.如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞

5.如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,
  读操作会返回FIFO中现有的数据量。

向FIFO中写入数据

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

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

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

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

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

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

FIFO的一些注意问题:

(1)管道数据的FIFO处理方式

          首先放入管道的数据,在端口首先被读出

(2)管道数据的不可再现性

          已读取的数据在管道里消失,不能再读

(3)管道长度的有限性

(4)SIGPIPE信号 如果一个进程试图写入到一个没有读取到进程的管道中哦你,系统内核产生SIGPIPE信号

实例1 :

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<string.h>
#define FIFO "/tmp/my_fifo"
main(int argc,char ** argv){
    char buf_r[100];
    int fd;
    int nread;
    if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))        //O_EXCL:可执行
        printf("cannot create FIFOserver\n");
    printf("Preparing for reading bytes...\n");
    memset(buf_r,0,sizeof(buf_r));
    fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);        //O_RDONLY:只读,O_NONBLOCK:非阻塞
    if(fd==-1){
        perror("open");
        exit(1);
    }
    while(1){
        memset(buf_r,0,sizeof(buf_r));
        if((nread=read(fd,buf_r,100))==-1){
            if(errno==EAGAIN)
                printf("no data yet\n");
        }
        printf("read %s from FIFO\n",buf_r);
        sleep(1);
    }
    pause();
    unlink(FIFO);
}

实例2 :

分别写两个程序,一个是服务器程序,不断从管道读取客户发送的信息;
另一个是客户程序,在命令行输入信息并从管道发送:

客户程序(写管道)

  1. /*fifo_write.c*/
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <errno.h>
  5. #include <fcntl.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. /*FIFO管道路径*/
  10. #define FIFO_SERVER "/tmp/myfifo"
  11. main(int argc,char** argv)  
  12. {  
  13. int fd = 0;  
  14. char w_buf[100];  
  15. int nwrite;  
  16. /*打开FIFO管道*/
  17.     fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);  
  18. if(fd==-1)  
  19. if(errno==ENXIO)  
  20.             printf("open error; no reading process\n");  
  21. /*判断有没有参数输入*/
  22. if(argc==1)  
  23.         printf("Please send something\n");  
  24. /*复制参数输入*/
  25.     strcpy(w_buf,argv[1]);  
  26. /*写到FIFO去*/
  27. if((nwrite=write(fd,w_buf,100))==-1)  
  28.     {  
  29. if(errno==EAGAIN)  
  30.             printf("The FIFO has not been read yet.Please try later\n");  
  31.     }  
  32. else
  33. /*输出写入的内容*/
  34.         printf("write %s to the FIFO\n",w_buf);  

服务程序(读管道)

   /*fifo_read.c*/

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. /*定义FIFO路径*/
  9. #define FIFO "/tmp/myfifo"
  10. main(int argc,char** argv)  
  11. {  
  12. char buf_r[100];  
  13. int  fd;  
  14. int  nread;  
  15. /*创建FIFO管道*/
  16. if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))  
  17.         printf("cannot create fifoserver\n");  
  18.     printf("Preparing for reading bytes...\n");  
  19.     memset(buf_r,0,sizeof(buf_r));  
  20. /*打开FIFO管道,不阻塞方式*/
  21.     fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);  
  22. if(fd==-1)  
  23.     {  
  24.         perror("open");  
  25.         exit(1);      
  26.     }  
  27. while(1)  
  28.     {  
  29.         memset(buf_r,0,sizeof(buf_r));  
  30. /*读管道,因为定义了非阻塞方式,故在此不会阻塞进程*/
  31. if((nread=read(fd,buf_r,100))==-1){  
  32. if(errno==EAGAIN)  
  33.                 printf("no data yet\n");  
  34.         }  
  35.         printf("read %s from FIFO\n",buf_r);  
  36.         sleep(1);  
  37.     }     
  38.     pause();  
  39.     unlink(FIFO);  

接下来进行编译,编译好后,在Linux中运行两个终端,分别运行以上两个程序,可以看到,运行fifo_read时,程序一直在每隔一秒读,然后我们在另一个终端输入:

$ ./fifo_write helloworld

可以看出fifo_read显示出“helloworld”,说明接受成功

下接下来进行编译,编译好后,在Linux中运行两个终端,分别运行以上两个程序,可以看到,运行fifo_read时,程序一直在每隔一秒读,然后我们在另一个终端输入:

$ ./fifo_write helloworld

可以看出fifo_read显示出“helloworld”,说明接受成功

posted @ 2009-11-09 17:22  jackyxm  阅读(11779)  评论(0编辑  收藏  举报