• linux 管道通信


    进程通信

    进程是系统分配资源的最小单位, 不同进程之间是相互隔离的, Linux常用于进程通信的几种方式有

    1. 匿名管道及有名管道 : 匿名管道用于具有亲缘关系的进程通信, 有名管道除此之外还可用于一般进程之间.
    2. 信号 : 软件层对中断机制的一种模拟.
    3. 消息队列 
    4. 共享内存 : 不同进程享同一块内存区域, 不同进程可以实时查看对方对共享内存的更新. 需要借助同步机制, 如互斥锁, 信号量.
    5. 信号量 : 主要用于不同进程以及同一进程不同线程的同步和互斥.
    6. 套接字 : 广泛用于网络间进程通信.

    无名管道

    管道是是基于文件描述符的通信方式, 无名管道只能用于具有亲缘关系之间的进程通信.  建立一个管道时 它会创建两个文件描述符, fd[0] 和 fd[1] , 其中 fd[0] 用于读取数据,  fd[1] 用于写入数据, 请看下图 :  

    如果父进程需要向子进程发送数据通信,  那么可以在父进程上创建一个管道,  关闭父进程的fd[0]和子进程的fd[1],  子进程向父进程发送数据就与之相反.

    下面演示了父进程向管道写入数据, 子进程从中读取.  sleep()函数确保父进程已经关闭了相应的文件描述符.

    #include<stdio.h>
    #include<sys/types.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<unistd.h>
    
    int main(void){
    
        pid_t pid;
        int pipe_fd[2];
        char buf[1024];
        const char data[] = "管道测试";
        int real_read, real_write;
    
        memset(buf, 0, sizeof(buf));
        /* 创建管道 */
        if(pipe(pipe_fd) < 0){
            perror("pipe");
            exit(1);
        }
    
        if((pid = fork()) < 0){
            perror("fork");
            exit(1);
        }else if(pid == 0){
            /* 子进程关闭写描述符, 等待1秒让父进程关闭读描述符*/
            close(pipe_fd[1]);
            sleep(2);
            if((real_read = read(pipe_fd[0], buf, 1024)) > 0){
                printf("读取管道内容 : %s
    ", buf);
            }
            close(pipe_fd[0]);
            exit(0);
        }else{
            /* 父进程关闭读描述符, 等待1秒让子进程关闭写描述符 */
            close(pipe_fd[0]);
            sleep(1);
            if((real_write = write(pipe_fd[1], data, strlen(data))) > 0){
                printf("写入管道内容 : %s
    ", data);
            }
            close(pipe_fd[1]);
            /* 收集子进程退出信息 */
            waitpid(pid, NULL, 0);
            exit(0);
        }
    }

    管道读写需要注意几点

    • 向管道写入数据时, 管道读端必须存在, 否则写进程将收到内核传来SIGPIPE信号.
    • 向管道写入数据, 不保证原子性, 如果读进程不读取管道缓冲区中的数据, 那么写进程会一直阻塞.
    • 父子进程运行是, 并不能保证先后顺序, 这里简单用sleep()解决.

    标准流管道

    相当系统调用,  用于创建连接到另一个进程之间的管道,  这里的进程是指可进行一定操作的可执行文件, 标准流管道把一系列创建过程合并到popen() 中了.

    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<fcntl.h>
    
    /*  标准管道流操作 */
    int main(void){
         FILE *fp;
         char *cmd = "ps -ef";
         char buf[1024];
    
         /* r 文件指针连接到command的标准输出*/
         if((fp = popen(cmd, "r")) == NULL){
              printf("popen error");
              exit(1);
         }
         while((fgets(buf, 1024, fp)) != NULL){
             printf("%s
    ", buf);
         }
         pclose(fp);
         exit(0);
    }

    有名管道

    Linux中专门设立了一个专门的特殊文件系统--管道文件,以FIFO的文件形式存在于文件系统中,这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据.但在磁盘上只是一个节点,而文件的数据则只存在于内存缓冲页面中,与普通管道一样.

    管道文件的读写可能有阻塞问题

    对于读进程

    1. 如果管道是阻塞打开, 且当前FIFO没有数据, 则读进程一直阻塞.
    2. 如果非阻塞打开, 立即执行读操作.

    对于写进程

    1. 如果管道是阻塞打开, 则一直阻塞到可以写入.
    2. 如果非阻塞打开而不能全部写入, 则写入部分或者写入失败. 

    下面包含两个程序, 一个用于读取管道, 并在该程序中创建管道, 另一个用于写管道.  首先要调用读程序, 创建一个管道.

    读程序

    #include<stdio.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<errno.h>
    #include<fcntl.h>
    #include<stdlib.h>
    #include<limits.h>
    
    /* 由读管道创建 */
    #define MYFIFO    "/tmp/myfifo"
    
    int main(int argc, char *argv[]){
    
        int fd;
        char buf[PIPE_BUF];
        int nread;
        
        /* 如果管道不存在则创建 */
        if(access(MYFIFO, F_OK) == -1){
            /* 0是管道文件, 666是权限 */
            if((mkfifo(MYFIFO, 0666) < 0) && (errno != EEXIST)){
                perror("create fifo");
                exit(1);
            }
        }
    
        /* 只读阻塞方式打开管道 */
        fd = open(MYFIFO, O_RDONLY);
        if(fd == -1){
            perror("open");
            exit(1);
        }
        
        /* 读取字符串 */
        while(1){
            memset(buf, 0, sizeof(buf));
            if((nread = read(fd, buf, PIPE_BUF)) > 0){
                printf("read %s
    ", buf);
            }
        }
    
        close(fd);
        exit(0);
    }

    写程序

    #include<stdio.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<errno.h>
    #include<fcntl.h>
    #include<stdlib.h>
    #include<limits.h>
    
    /* 由读管道创建 */
    #define MYFIFO    "/tmp/myfifo"
    
    int main(int argc, char *argv[]){
    
        int fd;
        char buf[PIPE_BUF];
        int nwrite;
    
        if(argc <= 1){
            printf("Usage: ./fifo_write <strring>");
            exit(1);
        }
        sscanf(argv[1], "%s", buf);
        
        /* 只写阻塞方式打开管道 */
        fd = open(MYFIFO, O_WRONLY);
        if(fd == -1){
            perror("open");
            exit(1);
        }
        
        /* 写入字符串 */
        if((nwrite = write(fd, buf, PIPE_BUF)) > 0){
            printf("write : %s
    ", buf);
        }
        close(fd);
        exit(0);
    }

    编译运行

    读程序
    $ gcc fifo_read.c -o fifo_read
    $ ./fifo_read
    read 写入1
    read 写入2
    read 写入3
    
    
    写程序
    $ gcc fifo_write.c -o fifo_write
    $ ./fifo_write
    $ ./fifo_write 写入1
    write : 写入1
    $ ./fifo_write 写入2
    write : 写入2
    $ ./fifo_write 写入3
    write : 写入3
  • 相关阅读:
    BZOJ3752 : Hack
    XIV Open Cup named after E.V. Pankratiev. GP of SPb
    XIII Open Cup named after E.V. Pankratiev. GP of Ukraine
    BZOJ2087 : [Poi2010]Sheep
    BZOJ2080 : [Poi2010]Railway
    BZOJ2082 : [Poi2010]Divine divisor
    Moscow Pre-Finals Workshop 2016. National Taiwan U Selection
    XIII Open Cup named after E.V. Pankratiev. GP of Asia and South Caucasus
    XIII Open Cup named after E.V. Pankratiev. GP of Azov Sea
    XIII Open Cup named after E.V. Pankratiev. GP of SPb
  • 原文地址:https://www.cnblogs.com/tanxing/p/6789199.html
Copyright © 2020-2023  润新知