zoukankan      html  css  js  c++  java
  • Linux学习笔记25——命名管道(FIFO)

    1 命名管道(FIFO)

          管道应用的一个重大缺陷就是没有名字,因此只能用于亲缘进程之间的通信。后来从管道为基础提出命名管道(named pipe,FIFO)的概念,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

    2 命名管道的创建

    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char *pathname,    //文件的路径
           mode_t mode          //和open函数mode一样
          );

      如果pathname路径下的文件已经存在,则mkfifo返回-1,errono将会返回EEXIST。

    3 命名管道操作

           FIFO在文件系统中表现为一个文件,大部分的系统文件调用都可以在FIFO上面,比如:read,open,write,close,unlink,stat等函数,但是seek函数不能对FIFO调用。

          可以调用open打开FIFO,请注意以下方面: 

               A 当以阻塞(未指定O_NONBLOCK)方式只读打开FIFO的时候,则将会被阻塞,直到有其他进程以写方式打开该FIFO。
               B 类似的,当以阻塞(未指定O_NONBLOCK)方式只写打开FIFO的时候,则将会被阻塞,直到有其他进程以读方式打开该FIFO
               C 当以非阻塞方式(指定O_NONBLOCK)方式只读打开FIFO的时候,则立即返回。当只写open时,如果没有进程为读打开FIFO,则返回-1,其errno是ENXIO
      如果写入管道的数据量小于等于PIPE_BUF,则系统保证write为原子操作,多个进程同时写管道,将不会出现穿插;如果写入数据量大于PIPE_BUF,则系统将不保证write为原子操作,多个进程同时写管道,则将有可能会穿插写入。这个规则在命名管道中继续适用。
     

    4 应用实例

         本例为一个client-server模式,服务器端将会创建一个闻名fifo文件(本例中为“/tmp/server”),并读取该fifo文件。客户端则打开该fifo文件,并把其请求写入该FIFO文件。服务器端读取该命令并执行之。

         服务器端的代码为:

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <ctype.h>
    
    #define SERVER_FIFO_NAME "/tmp/serv_fifo"
    #define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"
    #define BUFFER_SIZE 20
    
    struct data_to_pass_st{
        pid_t client_pid;
        char some_data[BUFFER_SIZE-1];
    };
    
    int main(){
        int server_fifo_fd,client_fifo_fd;
        struct data_to_pass_st mydata;
        int read_res;
        char client_fifo[256];
        char *tmp_char_ptr;
        
        mkfifo(SERVER_FIFO_NAME,0777);
        server_fifo_fd=open(SERVER_FIFO_NAME,O_RDONLY);
        if(server_fifo_fd==-1){
            fprintf(stderr,"Server fifo failure
    ");
            exit(EXIT_FAILURE);
        }
        sleep(10);
        do{
            read_res=read(server_fifo_fd,&mydata,sizeof(mydata));
            if(read_res>0){
                tmp_char_ptr=my_data.some_data;
                while(*tmp_char_ptr>0){
                    //把some_data中的所有字符全部转换为大写
                    *tmp_char_ptr=toupper(*tmp_char_ptr);
                    tmp_char_ptr++;
                }
                sprintf(client_fifo,CLIENT_FIFO_NAME,my_data.client_pid);
                client_fifo_fd=open(client_fifo,O_WRONLY);
                if(client_fifo_fd!=-1){
                    write(client_fifo_fd,&my_data,sizeof(my_data));
                    close(client_fifo_fd);
                }
                
            }
        }while(read_res>0);
        close(server_fifo_fd);
        unlink(SERVER_FIFO_NAME);
        exit(EXIT_SUCCESS);
    }

      客户端代码为:

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <ctype.h>
    
    #define SERVER_FIFO_NAME "/tmp/serv_fifo"
    #define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"
    #define BUFFER_SIZE 20
    
    struct data_to_pass_st{
        pid_t client_pid;
        char some_data[BUFFER_SIZE-1];
    };
    
    int main(){
        int server_fifo_fd,client_fifo_fd;
        struct data_to_pass_st my_data;
        int times_to_send;
        char client_fifo[256];
        
        server_fifo_fd=open(SERVER_FIFO_NAME,O_WRONLY);
        if(server_fifo_fd==-1){
            fprintf(stderr,"Sorry,no server
    ");
            exit(EXIT_FAILURE);
        }
        my_data.client_pid=getpid();
        sprintf(client_fifo,CLIENT_FIFO_NAME,my_data.client_pid);
        if(mkfifo(client_fifo,0777)==-1){
            fprintf(stderr,"Sorry,can't make %s
    ",client_fifo);
            exit(EXIT_FAILURE);
        }
        for(times_to_send=0;times_to_send<5;times_to_send++){
            sprintf(my_data.some_data,"Hello from %d",my_data.client_pid,my_data.some_data);
            write(server_fifo_fd,&my_data,sizeof(my_data));
            client_fifo_fd=open(client_fifo,O_RDONLY);
            if(client_fifo_fd!=-1){
                if(read(client_fifo_fd,&my_data,sizeof(my_data))>0){
                    printf("received:%s
    ",my_data.some_data);
                }
                close(client_fifo_fd);
            }
        }
        close(server_fifo_fd);
        unlink(client_fifo);
        exit(EXIT_SUCCESS);
        
    }

    5 总结

         与管道相比,FIFO最大的特点就是其在文件系统中有fifo文件存在,这样就可以做到进程间通信。

    6  FIFO的缺点
         当然FIFO也有它的局限性。客户端可以发请求到服务器,但前提是要知道一个公共的FIFO通道,对于实现服务器回传应答到客户端的问题,可以通过为每一个客户端创建一个专用的FIFO,来实现回传应答。但也有不足,服务器会同时应答成千上万个客户端,创建如此多的FIFO是否会使系统负载过大,相应的如何判断客户端是否因意外而崩溃成为难题,或者客户端不读取应答直接退出,所以服务器必须处理SIGPIPE信号,并做相应处理。(当没有进程为读打开FIFO文件时,write函数进行写操作会产生SIGPIPE信号)

  • 相关阅读:
    AGC002
    ICPC 北美Mid Central 2019 Regional
    【洛谷 5020】货币系统
    【洛谷 1109】学生分组
    【洛谷 2915】奶牛混合起来
    【洛谷 4162】最长距离
    【YCOJ 3805】竞选
    【洛谷 2807】最长路
    【洛谷 2918】买干草Buying Hay
    【LOJ 10172】涂抹果酱
  • 原文地址:https://www.cnblogs.com/zjzsky/p/3519705.html
Copyright © 2011-2022 走看看