Linux_ mkfifo 命名管道 操作

  1. 管道的缺点
    管道只能在具有“亲戚”关系的进程之间通信。
    即仅当管道由某个进程创建之后,在该进程的所有子孙进程之间,可通过该管道来通信。

    其他情况下的无此“亲戚”关系的进程不能使用管道通信。

    解决办法:使用命名管道

  2. 什么是命名管道?
    命名管道是一种特殊的文件,
    命名管道以普通文件的形式(在文件系统中有一个确定的路径和文件名)存在。
    任意进程只要使用该文件就能通信。

    注意:无名管道(即管道),是通过文件描述符的形式使用。
    命名管道,是通过该命令管道文件的“文件名”来使用。

  3. 命名管道的创建
    1) 使用mkfifo
    用法: man 3 mkfifo
    原型: int mkfifo(const char *pathname, mode_t mode);
    参数: mode,类似于open的第三个参数,表示该管道的访问权限。
    pathname, 表示该命名管道的绝对路径。
    返回值:成功,返回0
    失败,返回-1
    实例: main1.c

              查看已创建的管道
               (1) ls -l  /tmp/myfifo
                    第一个字符为p, 表示该文件是一个管道
               (2) ls -F /tmp/myfifo
                    最后一个字符为|, 表示该文件是一个管道
    

    2) 使用mknod
    用法: man 2 mknod
    原型:int mknod(const char *pathname, mode_t mode, dev_t dev);
    参数:mode, 一定要含有 | S_IFIFO
    注:该API能用来创建各种特殊文件。
    dev, 取0
    实例:main2.c
    返回值:成功,返回0
    失败,返回-1

  4. 命令管道的使用
    1) 通过shell命令使用
    假设已有命名管道“/tmp/fifo”,

    (1) 从命名管道读数据,如果没有数据,则将被阻塞
    # cat  /tmp/myfifo            
    
    (2) 在另一个终端上,向该命名管道发送数据
    # echo hello  > /tmp/myfifo
    
    (3) 第(1)中的终端,将读取到数据。
    

    2) 通过open使用命名管道
    (1) open命名管道与open普通文件之间的区别:

          区别1:
         open命名管道时,不能以O_RDWR方式打开,只能以只读或只写方式打开。
                                  因为命名管道是单向的。
                                  如果以读写方式打开,结果将不可预期。
    
                                 如果要使用命名管道进行双向数据传输,只能:
                                  a) 创建两个命名管道,分别以不同方向打开。
                                  b) 只创建一个命名管道,
                                       先使用一个方向进行open,传递数据之后,再关闭。
                                       然后再用反方向方式打开,进行反方向传输。
    
       区别2:
       open命名管道时,O_NONBLOCK的意义与普通文件不同。
    
    
         区别3:
         open命名管道时,具有同步作用
    
    (2) open命名管道的合法方式:
         a) 只读阻塞式open
             open(MYFIFO,  O_RDONLY);
             效果:open操作将被阻塞,直到其他进程以写方式打开该FIFO
    
          b) 只写阻塞式open
              write(MYFIFO,  O_WRONLY);
              效果:open操作将被阻塞,直到其他进程以读方式打开该FIFO
    
          c) 只读非阻塞式open
              open(MYFIFO,  O_RDONLY | O_NONBLOCK);
              效果:马上成功返回,无论其他进程是否以写方式打开该FIFO
    
          d) 只写非阻塞式open
              open(MYFIFO,  O_WRONLY | O_NONBLOCK);
              效果:马上返回,无论其他进程是否以读方式打开该FIFO
                       如果其他进程没有以读方式打开该FIFO, 则返回-1,该FIFO打开失败。
                        如果其他进程已经以读方式打开该FIFO, 则返回一个文件描述符,打开成功。
    
         注意:对于FIFO, O_NONBLOCK对于只读和只写有不同的意义!
                  对于普通文件,O_NONBLOCK对于读和写的含义相同。
    
    1. 多进程使用FIFO时的常用组合方式
      多进程在打开FIFO时,能够获得“同步”。

      1) 进程A以只读阻塞式打开FIFO
      进程B以只写阻塞式打开FIFO
      最常用。

      2) 进程A以只读非阻塞式打开FIFO
      进程B以只写阻塞式打开FIFO

      3) 其他组合方式,不常用。

      实例:main3_r.c
      main3_w.c
      在不同终端上运行。

  5. 命名管道的读写
    1)读命名管道
    如果FIFO是以阻塞式打开的(即没有使用O_NONBLOCK)
    read时,如果当前该FIFO中没有数据,则阻塞read, 直到FIFO中被写入数据。
    如果当前该FIFO中有数据,则读取数据后返回。

     如果FIFO是以非阻塞式打开的(即使用O_NONBLOCK)
           read时,如果当前该FIFO中没有数据,则马上返回0。
                        如果当前该FIFO中有数据,则读取数据后返回。
    
     如果关闭了FIFO的写端文件描述符,则read直接返回0(无论是否是阻塞式read)
     该特性和两端口的管道相同。
    

    2) 写命名管道
    如果FIFO是以阻塞式打开的(即没有使用O_NONBLOCK)
    write时,如果当前该FIFO不能再写入数据时, 则阻塞write, 直到FIFO可被写入数据。
    如果当前该FIFO还能写入数据,则写数据后返回。

           注:该方式下,如果要写入的数据长度 <= PIPE_BUF, 则要么全部写入,要么都不写入。
    
    
     如果FIFO是以非阻塞式打开的(即使用O_NONBLOCK)
           write时候,如果该FIFO当前还能容纳要写入的全部数据,则写入全部数据后返回。
                           如果该FIFO当前已不能容纳要写入的全部数据,则
                                  如果要写入的数据长度 > PIPE_BUF ,    则写入部分数据,然后返回。
                                                                                         返回值为实际写入的字节数,也可能返回0
                                  如果要写入的数据长度 <= PIPE_BUF ,  则不写入任何数据,然后返回。
    
     如果关闭了FIFO的读端文件描述符,则write可能产生异常,而直接终止程序
    

    3) 写FIFO时的“原子性”
    如果写操作都是阻塞式的,而且每次要写入的数据长度 <= PIPE_BUF,
    那么,各进程的对该FIFO的写操作不会”交错”, 即都是原子性的。

    其他情况,可能发生“交错”。

    所以,每次要写入的字节数最好设置为PIPE_BUF。
    注意:PIPE_BUF在limits.h中定义

    1. 使用命名管道实现IPC
      注意:使用FIFO,进行进程间通信时存在“两级”同步:
      打开时,同步。
      读写时,同步。

      实例:main4.c
      进程A向进程B传送10M数据。
      并测试进程B读取10M数据所耗时间。

      练习:main5.c
      进程A循环等待用户输入,
      用户每输入一个单词后,就把该单词用管道发送给进程B,
      直到用户输入exit。

      进程B每收到一个单词后,就统计该单词的长度并打印输出
      直到进程A结束输入。

  6. 使用命令管道来创建服务器和客户端
    代码:
    版本1:客户端关闭时,服务器不关闭
    day5/server_client/
    版本2:客户端关闭时,服务器也关闭。
    day5/server.c
    day5/client.c

你可能感兴趣的:(Linux)