首页 > 系统相关 >linux命名管道

linux命名管道

时间:2023-06-01 18:05:36浏览次数:46  
标签:int FIFO 管道 fd argv linux 命名 include


管道是进程间通信的主要手段之一。一个管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端。管道是一种特殊的文件,它不属于某一种文件系统,而是一种独立的文件系统,有其自己的数据结构。根据管道的适用范围将其分为:无名管道和命名管道。
●无名管道
主要用于父进程与子进程之间,或者两个兄弟进程之间。在linux系统中可以通过系统调用建立起一个单向的通信管道,且这种关系只能由父进程来建立。因此,每个管道都是单向的,当需要双向通信时就需要建立起两个管道。管道两端的进程均将该管道看做一个文件,一个进程负责往管道中写内容,而另一个从管道中读取。这种传输遵循“先入先出”(FIFO)的规则。
●命名管道
命名管道是为了解决无名管道只能用于近亲进程之间通信的缺陷而设计的。命名管道是建立在实际的磁盘介质或文件系统(而不是只存在于内存中)上有自己名字的文件,任何进程可以在任何时间通过文件名或路径名与该文件建立联系。为了实现命名管道,引入了一种新的文件类型——FIFO文件(遵循先进先出的原则)。实现一个命名管道实际上就是实现一个FIFO文件。命名管道一旦建立,之后它的读、写以及关闭操作都与普通管道完全相同。虽然FIFO文件的inode节点在磁盘上,但是仅是一个节点而已,文件的数据还是存在于内存缓冲页面中,和普通管道相同。
环形缓冲区
每个管道只有一个页面作为缓冲区,该页面是按照环形缓冲区的方式来使用的。这种访问方式是典型的“生产者——消费者”模型。当“生产者”进程有大量的数据需要写时,而且每当写满一个页面就需要进行睡眠等待,等待“消费者”从管道中读走一些数据,为其腾出一些空间。相应的,如果管道中没有可读数据,“消费者”进程就要睡眠等待,

有名管道的介绍

无名管道,由于没有名字,只能用于亲缘关系的进程间通信.。为了克服这个缺点,提出了有名管道(FIFO)。

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

注意:有名管道的名字存在于文件系统中,内容存放在内存中。

有名管道的创建

linux命名管道_#include

该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode参数相同。如果mkfifo的一个参数是一个已经存在路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。

有名管道的打开规则

有名管道比无名管道多了一个打开操作:open

FIFO的打开规则:

如果当前打开操作时为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。

如果当前打开操作时为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENIO错误(当期打开操作没有设置阻塞标志)。

/************
打开fifo
 写函数
***************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
    int fd;

    if(argc < 2)
    {
        fprintf(stderr,"usage : %s argv[1].\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
    {
        fprintf(stderr,"Fail to mkfifo %s : %s.\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    if((fd = open(argv[1],O_WRONLY)) < 0)
    {
        fprintf(stderr,"Fail to open %s : %s.\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    printf("open for write success.\n");

    return 0;
}

//打开读fifo文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
    int fd;

    if(argc < 2)
    {
        fprintf(stderr,"usage : %s argv[1].\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
    {
        fprintf(stderr,"Fail to mkfifo %s : %s.\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    if((fd = open(argv[1],O_RDONLY)) < 0)
    {
        fprintf(stderr,"Fail to open %s : %s.\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    printf("open for read success.\n");

    return 0;
}

如果open时没有使用O_NONBLOCK参数,我们发现不论读端还是写端先打开,先打开者都会阻塞,一直阻塞到另一端打开。

有名管道的读写规则

A.从FIFO中读取数据

约定:如果一个进程为了从FIFO中读取数据而以阻塞的方式打开FIFO, 则称内核为该进程的读操作设置了阻塞标志

<1>如果有进程为写而打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说返回-1,当前errno值为EAGAIN,提醒以后再试。

<2>对于设置阻塞标志的读操作说,造成阻塞的原因有两种:当前FIFO内有数据,但有其他进程正在读这些数据;另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论写入数据量的大小,也不论读操作请求多少数据量。

<3>如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞

<4>如果写端关闭,管道中有数据读取管道中的数据,如果管道中没有数据读端将不会继续阻塞,此时返回0。

注意:如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作会返回FIFO中现有的数据量。

B.向FIFO中写入数据

约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作设置了阻塞标志。

对于设置了阻塞标志的写操作:

<1>当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳写入的字节数时,才开始进行一次性写操作。

<2>当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。

对于没有设置阻塞标志的写操作:

<1>当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。

<2>当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写。

注意:只有读端存在,写端才有意义。如果读端不在,写端向FIFO写数据,内核将向对应的进程发送SIGPIPE信号(默认终止进程);

//向fifo写入数据

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAX 655360

int main(int argc,char *argv[])
{
    int n,fd;
    char buf[MAX];

    if(argc < 2)
    {
        fprintf(stderr,"usage : %s argv[1].\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)  //创建fifo文件
    {
        fprintf(stderr,"Fail to mkfifo %s : %s.\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    if((fd = open(argv[1],O_WRONLY )) < 0)        //打开fifo写
    {
        fprintf(stderr,"Fail to open %s : %s.\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    printf("open for write success.\n");

    while(1)
    {
        printf(">");
        scanf("%d",&n);             //输入数据

        n = write(fd,buf,n);        //写入数据   
        printf("write %d bytes.\n",n);   //打印写入多少字节
    }

    exit(EXIT_SUCCESS);
}

//从fifo读数据
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAX 655360

int main(int argc,char *argv[])
{
    int fd,n;
    char buf[MAX];

    if(argc < 2)
    {
        fprintf(stderr,"usage : %s argv[1].\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)  //创建fifo文件
    {
        fprintf(stderr,"Fail to mkfifo %s : %s.\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    if((fd = open(argv[1],O_RDONLY )) < 0)    //打开fifo为只读
    {
        fprintf(stderr,"Fail to open %s : %s.\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    printf("open for read success.\n");     

    while(1)
    {
        printf(">");
        scanf("%d",&n);

        n = read(fd,buf,n);

        printf("Read %d bytes.\n",n);

    }

    exit(EXIT_SUCCESS);
}

实例分析

1.创建txt文档,向文档里写入数据

/*******************
创建txt文档,名为data.txt
向文档里写入1000遍buffer保存入文档

*********************/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void filecopy(FILE *,char *);   //函数声明

int main(void)
{
    FILE *fp1;
    long int i = 1000;
    char buf[] = "Welcome to coolwriter's blog!\n";
    char *file1 = "data.txt";

    printf("begin!\n");

    if((fp1 = fopen(file1,"a+")) == NULL ) //"a+" 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
    {      
            printf("can't open %s\n",file1);
    }
    while(i--)
    filecopy(fp1,buf);    // 循环调用1000遍filecopy函数

    fclose(fp1);         //关闭文件

    printf("over!\n");     //打印“结束”

    return 0;
}

void filecopy(FILE *ifp,char *buf)   //写数据函数,其中*ifp文件指针,*buf数据
{
    char c;
    int i,j;
    j = 0;
    i = strlen(buf)-1;          //除去最后字符'/n'
    while(i--){                 //循环buf的长度遍
        putc(buf[j],ifp);       //向文件ifp写入字符,每遍写入一个字符
        j++;                 //移动buf数组中的字符位置号
    }
    putc('\n',ifp);         //向文件ifp写入字符'/n'
}

/*******************
创建fifo
把数据文件 data.txt中的数据
 写入到fifo管道           
*********************/
#include <unistd.h>  
#include <stdlib.h>  
#include <fcntl.h>  
#include <limits.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <stdio.h>  
#include <string.h>  

int main()  
{  
    const char *fifo_name = "my_fifo";    //定义fifo文件
    char *file1 = "data.txt";            //定义数据文件
    int pipe_fd = -1;  
    int data_fd = -1;  
    int res = 0;  
    const int open_mode = O_WRONLY;      
    int bytes_sent = 0;  
    char buffer[PIPE_BUF + 1];  

    if(access(fifo_name, F_OK) == -1)  //判断文件是否存在
    {  
        //管道文件不存在  
        //创建命名管道  
        res = mkfifo(fifo_name, 0777);    //创建fifo管道
        if(res != 0)  
        {  
            fprintf(stderr, "Could not create fifo %s\n", fifo_name);  
            exit(EXIT_FAILURE);  
        }  
    }  

    printf("Process %d opening FIFO O_WRONLY\n", getpid());  //打印进程号 

    pipe_fd = open(fifo_name, open_mode);  //以只写入阻塞方式打开FIFO文件
    data_fd = open(file1, O_RDONLY);       //以只读方式打开数据文件 

    printf("Process %d result %d\n", getpid(), pipe_fd);  //打印进程号 

    if(pipe_fd != -1)  
    {  
        int bytes_read = 0;  
        //向数据文件读取数据  
        bytes_read = read(data_fd, buffer, PIPE_BUF); //PIPE_BUF在/usr/include/linux/limits.h中定义 4096bytes
        buffer[bytes_read] = '\0';  //最后数组的结束符

        while(bytes_read > 0)       //读写成功
        {  
            //向FIFO文件写数据  
            res = write(pipe_fd, buffer, bytes_read);  //读写成功
            if(res == -1)  
            {  
                fprintf(stderr, "Write error on pipe\n");  
                exit(EXIT_FAILURE);  
            }  
            //累加写的字节数,并继续读取数据  
            bytes_sent += res;  
            bytes_read = read(data_fd, buffer, PIPE_BUF);  
            buffer[bytes_read] = '\0';  
        }  
        close(pipe_fd);  
        close(data_fd);  
    }  
    else  
        exit(EXIT_FAILURE);  

    printf("Process %d finished\n", getpid());  
    exit(EXIT_SUCCESS);  
}

/*******************
创建文档DataFormFIFO.txt
把fifo管道中的数据读出来
保存到DataFormFIFO.txt文档里面            
*********************/
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <fcntl.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <limits.h>  
#include <string.h>  

int main()  
{  
    const char *fifo_name = "my_fifo";  
    int pipe_fd = -1;  
    int data_fd = -1;  
    int res = 0;  
    int open_mode = O_RDONLY;  
    char buffer[PIPE_BUF + 1];  
    int bytes_read = 0;  
    int bytes_write = 0;  

    memset(buffer, '\0', sizeof(buffer));  //清空缓冲数组 

    printf("Process %d opening FIFO O_RDONLY\n", getpid());  

    pipe_fd = open(fifo_name, open_mode);   //以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名  

    data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);  //以只写方式创建打开保存数据的文件 
    printf("Process %d result %d\n",getpid(), pipe_fd);  

    if(pipe_fd != -1)  
    {  
        do  
        {  
            //读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中  
            res = read(pipe_fd, buffer, PIPE_BUF);  
            bytes_write = write(data_fd, buffer, res);  
            bytes_read += res;  
        }while(res > 0);  
        close(pipe_fd);  
        close(data_fd);  
    }  
    else  
        exit(EXIT_FAILURE);  

    printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);  
    exit(EXIT_SUCCESS);  
}


标签:int,FIFO,管道,fd,argv,linux,命名,include
From: https://blog.51cto.com/u_16147764/6397226

相关文章

  • linux消息队列
    经典进程间通信机制(IPC):管道、FIFO、消息队列、信号量以及共享储存。这些机制允许在同一台计算机上运行的进程可以相互通信。但是当考察到不同计算机(通过网络相连)的进程相互通信时就必须借助网络通信机制(networkIPC),在分布式计算环境中,为了集成分布式应用,开发者需要对异构网络环境......
  • VMware中安装Linux-kali
    VMware中安装Linux-kali(详细图文教程)萌褚于2022-05-2010:33:30发布9499收藏94文章标签:Linux版权华为云开发者联盟该内容已被华为云开发者联盟社区收录加入社区镜像下载、域名解析、时间同步请点击阿里云开源镜像站一,VMware配置。  因为要装kali,所以要用到Debian......
  • Linux进程管理、计划任务笔记
    一、Linux进程管理1.1、进程概念进程是正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说CPU(寄存器),IO,内存,网络资源等。并发程序和顺序程序有本质上的差别,为了能更好地描述程序的并发执行,实现操作系统的并发性和共享性,引入“进程”的概念。进程是具有一定独立......
  • Linux系统管理---时区和时间管理
    一、时区查看时区:cat/etc/sysconfig/clock时区存放文件:/etc/localtime修改时区:cp/usr/share/zoneinfo/Asia/Shanghai /etc/localtime修改验证:date  返回有CST,表示修改正确了二、时间查看时间:date 或者  date-R或者按指定格式:date"+%Y-%m-%d%H:%M:%S"[root@hadoo......
  • Linux软件安装--二进制发布包安装、rpm发布包安装(案例:jdk和mysql安装)
    Linux软件安装的4种方式一、二进制发布包指软件已经根据平台编译并且打包,拿到这个包后解压并配置环境变量,如jdk包、mysql包、Tomcat包。示例:二进制发布包安装jdk。示例:二进制发布包安装jdk1.获取安装包1).直接从linux网上下载安装包-->wget https://download.oracle.com/otn-pu......
  • Linux的SSH免密登录配置
    一、SSH概念SSH为SecureShell(安全外壳协议)的缩写,简单说,SSH只是一种网络协议,用于计算机之间的加密登录传输,很多ftp、pop和telnet在本质上都是不安全的,因为它们在网络上用明文传送口令和数据,别有用心的人非常容易就可以截获这些口令和数据。而SSH就是专为远程登录会话和其他网络服务......
  • linux配置静态ip
    转:https://blog.csdn.net/weixin_46560589/article/details/1248148601进入配置文件目录cd/etc/sysconfig/network-scripts 2编辑配置文件vimifcfg-ens32ens32是虚拟网卡名称,根据自己虚机上的网卡名称修改如果有多个网卡,编辑你要用的那个网卡 3......
  • linux 中 删除指定匹配特定字符指定次数之前或者之后的内容
     001、[root@PC1test]#lsa.txt[root@PC1test]#cata.txt0102030405060708091011121314151617181920[root@PC1test]#sed's//tag/2'a.txt|sed's/.*tag//'##删除第二个空格之前的所有内容030405080910131415181920......
  • linux下查看 SELinux状态及关闭SELinux
    SELinux全称为安全增强式Security-EnhancedLinux(SELinux),是一个在内核中实践的强制存取控制(MAC)安全性机制。SELinux首先在CentOS4出现,并在其后的CentOS发行版本获得重大改善。这些改善代表用SELinux解决问题的方法亦随著时间而改变。SELinux的原理与架构SELinux的整体......
  • Linux centos7 ppc64le编译安装MySQL8遇见问题
    一.关于Nopackagedevtoolset-7-gccavailable.的解决办法1.使用centos默认yum源2.依次执行以下命令yuminstall-ycentos-release-sclyuminstall-ydevtoolset-7 二.cmake3>=3.6.1isneededbymysql-community-8.0.18-1.el7.ppc64le安装cmake3yuminstall......