zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    CentOS 7安装Splunk
    OpenSwitch操作系统成为Linux基金会官方项目
    新手选择使用 Linux 桌面的七个注意点
    SELinux入门
    新一代 Tor发布,它牛在哪里?
    【光环国际】一位老太太的需求
    【情商】为人处世
    【Teradata】磁盘碎片整理(ferret工具)
    【架构解密】第六章 深入解析分布式存储
    【大数据技术】HBase Meetup资料
  • 原文地址:https://www.cnblogs.com/tanxing/p/6789199.html
Copyright © 2011-2022 走看看