zoukankan      html  css  js  c++  java
  • Linux进程间通信---管道和有名管道

    一、管道

        管道:管道是一种半双工的通信方式,数据只能单方向流动,而且只能在具有亲缘关系的进程间使用,因为管道

    传递数据的单向性,管道又称为半双工管道。进程的亲缘关系通常是指父子进程关系。

        管道的特点决定了其使用的局限性:

    • 数据只能由一个进程流向另一个进程(其中一个为写管道,另一个为读管道);如果要进行全双工通信,需要

    建立两个管道。

    • 管道只能用于父子进程或者兄弟进程间的通信,也就是说管道只能用于具有亲缘关系的进程间的通信,无亲缘

    关系的进程不能使用管道。

        管道的创建:

        Linux下创建管道可以通过函数pipe来完成。该函数如果调用成功则返回0,并且数组中将包含两个新的文件描述符;

    如果有错误发生,返回-1,该函数返回两个文件描述符:pipefd[0]和pipefd[1]。前者打开来读,后者打开来写。该函

    的原型为:

        #include <fcntl.h>

        #include <unistd.h>

        int pipe(int pipefd[2]);

    1. 下面是通过建立管道和创建父子进程间的通信:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/wait.h>
    
    int main()
    {
        int fd[2];
        pid_t pid;
        int ret;
        ret= pipe(fd);
        if(ret== -1)
        {
            perror("pipe.
    ");
            exit(1);
        }
        pid= fork();
        if(pid== -1)
        {
            perror("fork.
    ");
            exit(1);
        }
        else if(pid== 0)
        {
            char buff[256];
            close(fd[1]);
            read(fd[0],buff,256);
            printf("Form parent say: %s
    ",buff);
            close(fd[0]);
        }
        else
        {
            char *say= "Hello Linux.";
            close(fd[0]);
            write(fd[1],say,strlen(say)+ 1);
            close(fd[1]);
            int status;
            wait(&status);
        }
        return 0;
    }

                                          

        这是父子进程间利用管道进行通信的一个例子。

    2. 下面是通过建立两个管道来实现父子进程间全双工通信:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    
    int main()
    {
        char *parent_talk[]= {"Hello","Can you tell me what time is it?","Ok,I must go,Bye",NULL};
        char *child_talk[]= {"Hi","No problem","See you,Bye",NULL};
        int fd1[2],fd2[2];
        pid_t pid;
        int ret;
        ret= pipe(fd1);
        if(ret== -1)
        {
            perror("pipe1.
    ");
            exit(1);
        }
        ret= pipe(fd2);
        if(ret== -1)
        {
            perror("pipe2.
    ");
            exit(1);
        }
        pid= fork();
        if(pid== -1)
        {
            perror("fork.
    ");
            exit(1);
        }
        else if(pid== 0)
        {
            char buff[256];
            close(fd1[1]);
            close(fd2[0]);
            int i= 0;
            char *talk= child_talk[i];
            while(talk!= NULL)
            {
                read(fd1[0],buff,256);
                printf("Parent say: %s
    ",buff);
                write(fd2[1],talk,strlen(talk)+ 1);
                talk= child_talk[++i];
            }
            close(fd1[0]);
            close(fd2[1]);
        }
        else 
        {
            char buff[256];
            close(fd1[0]);
            close(fd2[1]);
            int i= 0;
            char *talk= parent_talk[i];
            while(talk!= NULL)
            {
                write(fd1[1],talk,strlen(talk)+ 1);
                read(fd2[0],buff,256);
                printf("Child say: %s
    ",buff);
                talk= parent_talk[++i];
            }
            close(fd1[1]);
            close(fd2[0]);
            int status;
            wait(&status);
        }
        return 0;
    }

                                 

        这是通过建立两个管道来实现父子进程间全双工通信的例子。

    二、有名管道      

        管道的有一个不足之处是没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(FIFO)提出后,该

    限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。有名

    管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相

    互通信。需要注意的是,FIFO总是按照先进先出的原则工作,第一个被写入的数据将首先从管道中读出。

        有名管道的创建:

        Linux下有两种方式创建有名管道。一是在Shell下交互地建立一个有名管道,二是在程序中使用系统函数建立有

    管道。Shell方式下可使用mkfifo或mknod命令。创建有名管道的系统函数有两个:mkfifo和mknod。两个函数均定义

    头文件sys/stat.h中,函数的原型为:

        #include <sys/types.h>
        #include <sys/stat.h>
        int mkfifo(const char *pathname,mode_t mode);

        int mknod(const char *pathname,mode_t mode,dev_t dev);

        函数mkfifo参数中pathname为创建的有名管道的全路径名;mode为创建的有名管道的模式,指明其存取权限;函

    数mknod参数中pathname为创建的有名管道的全路径名;mode为创建的有名管道的模式,指明其存取权限;dev为设

    备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到。这两个函数调用成功都返回0,失败都返回-1。

        下面是使用有名管道建立一个客户端/服务器的例子:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #define BUFFER_SIZE 256
    const char *write_fifo= "write_fifo";
    const char *read_fifo= "read_fifo";
    
    int main()
    {
        if(access(write_fifo,F_OK))
        {
            int ret= mkfifo(write_fifo,0755);
            if(ret== -1)
            {
                perror("mkfifo");
                exit(1);
            }
        }
        int write_fd= open(write_fifo,O_WRONLY);
        if(write_fd== -1)
        {
            perror("open write_fifo.");
            exit(1);
        }
        int read_fd;
        while(1)
        {
            read_fd= open(read_fifo,O_RDONLY);
            if(read_fd== -1)
            {
                sleep(1);
                continue;
            }
            break;
        }
        char sendbuf[BUFFER_SIZE];
        char recvbuf[BUFFER_SIZE];
        while(1)
        {
            printf("Ser:");
            scanf("%s",sendbuf);
            if(strcmp(sendbuf,"quit")== 0)
            {
                unlink(write_fifo);
                break;
            }
            write(write_fd,sendbuf,strlen(sendbuf)+ 1);
            read(read_fd,recvbuf,BUFFER_SIZE);
            printf("Cli:%s
    ",recvbuf);
        }
        close(write_fd);
        close(read_fd);
        return 0;
    }
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #define BUFFER_SIZE 256
    const char *write_fifo= "write_fifo";
    const char *read_fifo= "read_fifo";
    
    int main()
    {
        int read_fd= open(write_fifo,O_RDONLY);
        if(read_fd== -1)
        {
            perror("open write_fifio.");
            exit(1);
        }
        if(access(read_fifo,F_OK))
        {
            int ret= mkfifo(read_fifo,0755);
            if(ret== -1)
            {
                perror("mkfifo");
                exit(1);
            }
        }
        int write_fd= open(read_fifo,O_WRONLY);
        if(write_fd== -1)
        {
            perror("open read_fifo.");
            exit(1);
        }
        char sendbuf[BUFFER_SIZE];
        char recvbuf[BUFFER_SIZE];
        while(1)
        {
            read(read_fd,recvbuf,BUFFER_SIZE);
            printf("Ser:%s
    ",recvbuf);
            printf("Cli:");
            scanf("%s",sendbuf);
            if(strcmp(sendbuf,"quit")== 0)
            {
                unlink(read_fifo);
                break;
            }
            write(write_fd,sendbuf,strlen(sendbuf)+ 1);
        }
        close(write_fd);
        close(read_fd);
        return 0;
    }

                                                    

  • 相关阅读:
    将python list数据结果存为html
    英文分词对比nltk vs spacy
    dict读取字典的第一个值-python
    分批查询数据库数据存入文件
    jupyter lab中显示gym的游戏动图
    模型融合
    读取csv中某列转为数字,顺序不变
    tensorflow和pytorch中的自定义网络层理解
    【小白刷题之路Day31】leetcode768. 最多能完成排序的块 II (动态规划、单调栈(没弄懂))
    【小白刷题之路Day31】leetcode769. 最多能完成排序的块(滑动窗口法、特殊使用、极致优化)
  • 原文地址:https://www.cnblogs.com/XNQC1314/p/9017531.html
Copyright © 2011-2022 走看看