zoukankan      html  css  js  c++  java
  • Linux学习记录--命名管道通信

    命名管道通信

    什么是命名管道

    一个主要的限制是,它是匿名管道的应用还没有名字,因此,只有它可以用于进程间通信的方式与亲缘关系。在命名管道(named pipeFIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。

    这样,即使与FIFO的创建进程不存在亲缘关系的进程,仅仅要可以訪问该路径,就行彼此通过FIFO相互通信

     

    有名管道创建

    int mkfifo(const char * pathname, mode_t mode)

     和普通文件创建一样pathname为文件名,mode为权限

    有名管道通信规则

    管道关闭规则

    intclose (int __fd);

    1.当最后一个读进程管道关闭时。写进程不管是堵塞还是非堵塞,都会将管道写满(假设能写满)并退出

    2.当最后一个写进程管道关闭时,向管道写入一个结束标识,当读进程从管道读到这个结束标识时。假设是堵塞读进程将结束堵塞返回读入数据个数为0.(对于未堵塞读进程假设管道内没有数据则返回-1,假设读到结束标识则返回读入数据个数为0

     

    规则分析1

    写进程:

    #include<unistd.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<fcntl.h>
    #include<limits.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<errno.h>
    
    #define FIFO_NAME "/tmp/my_fifo"
    #define BUF_SIZE 80000
    intmain(int argc,char *argv[])
    {
    	unlink(FIFO_NAME);
    int pipe_fd;
    int res;
    char buf[BUF_SIZE];
    memset(buf,3,BUF_SIZE);
    if(access(FIFO_NAME,F_OK)==-1)
        {
            res=mkfifo(FIFO_NAME,0766);
    if(res!=0)
            {
    fprintf(stderr,"不能创建管道文件 %s
    ",FIFO_NAME);
    exit(1);
            }
        }
    
    printf("进程PID %d 打开管道 O_WRONLY
    ",getpid());
        pipe_fd=open(FIFO_NAME,O_WRONLY);
    
    if(pipe_fd!=-1)
        {
            res=write(pipe_fd,buf,sizeof(buf));
    printf("写入数据的大小是%d 
    ",res);
    close(pipe_fd);
    sleep(1000);
        }
    else
    exit(1);
    exit(1);
    }
    

    读进程

    #include<unistd.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<fcntl.h>
    #include<limits.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    
    #define FIFO_NAME "/tmp/my_fifo"
    #define BUF_SIZE 20
    
    intmain(int argc, char *argv[]) {
    	char buf[BUF_SIZE];
    	memset(buf, 0, BUF_SIZE);
    	int pipe_fd;
    	int res;
    	int bytes_read = 0;
    
    	printf("进程PID %d 打开管道 O_RDONLY
    ", getpid());
    	pipe_fd = open(FIFO_NAME, O_RDONLY);
    
    	if (pipe_fd != -1) {
    		bytes_read = read(pipe_fd, buf, sizeof(buf));
    		printf("读入数据的大小是%d 
    ", bytes_read);
    		sleep(10);
    		close(pipe_fd);
    	} else
    		exit(1);
    	exit(1);
    }

    控制台信息

     

    读进程:

    进程PID 10930打开管道 O_RDONLY

    读入数据的大小是20

     

    写进程:

    进程PID 10918打开管道 O_WRONLY

    10S后输出…..

    写入数据的大小是65536

     

    分析:当读进程运行到close(pipe_fd);时,写进程一次性将数据写满缓冲区(65536)并退出。

     

    规则分析2

    写进程

    #define FIFO_NAME "/tmp/my_fifo"
    #define BUF_SIZE 80000
    int main(int argc,char *argv[])
    {
    	unlink(FIFO_NAME);
    int pipe_fd;
    int res;
    char buf[BUF_SIZE];
    memset(buf,3,BUF_SIZE);
    if(access(FIFO_NAME,F_OK)==-1)
        {
            res=mkfifo(FIFO_NAME,0766);
    if(res!=0)
            {
    fprintf(stderr,"不能创建管道文件 %s
    ",FIFO_NAME);
    exit(1);
            }
        }
    
    printf("进程PID %d 打开管道 O_WRONLY
    ",getpid());
        pipe_fd=open(FIFO_NAME,O_WRONLY);
    
    if(pipe_fd!=-1)
        {
            res=write(pipe_fd,buf,sizeof(buf));
    printf("写入数据的大小是%d 
    ",res);
    sleep(10);
    close(pipe_fd);
    
        }
    else
    exit(1);
    exit(1);
    }
    
     

    读进程

    #define FIFO_NAME "/tmp/my_fifo"
    #define BUF_SIZE 4000
    
    int main(int argc, char *argv[]) {
    	char buf[BUF_SIZE];
    	memset(buf, 0, BUF_SIZE);
    	int pipe_fd;
    
    	int bytes_read = 0;
    
    	printf("进程PID %d 打开管道 O_RDONLY
    ", getpid());
    	pipe_fd = open(FIFO_NAME, O_RDONLY);
    
    	if (pipe_fd != -1) {
    
    		do {
    			bytes_read = read(pipe_fd, buf, sizeof(buf));
    			printf("读入数据的大小是%d 
    ", bytes_read);
    
    		} while (bytes_read != 0);
    
    		close(pipe_fd);
    	} else
    		exit(1);
    	exit(1);
    }

    控制台输出:

     

    读进程

    进程PID 12240打开管道 O_RDONLY

    读入数据的大小是4000.

    ……

    (10S)

    读入数据的大小是0

     

    写进程

    进程PID 12227打开管道 O_WRONLY

    写入数据的大小是80000

     

    分析:

    假设读进程为堵塞的,当写进程关闭管道时。读进程收到写进程发来的结束符,读进程结束堵塞(此时bytes_read =0

    假设读进程为非堵塞的,首先将全部数据读取出来,然后在读进程未收到写进程发来的结束符时。由于管道没有数据读进程不会堵塞且返回-1,由于此例WHILE退出条件是bytes_read =0。因此在未读到结束符之前返回值一直是-1,直到读取到结束符才返回0

    管道写端规则

     

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

    1.       当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。假设此时管道空暇缓冲区不足以容纳要写入的字节数。则进入睡眠。直到当缓冲区中可以容纳要写入的字节数时,才開始进行一次性写操作。

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

     

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

    3.       当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

    在写满全部FIFO空暇缓冲区后,写操作返回。

    4.       当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。

    假设当前FIFO空暇缓冲区可以容纳请求写入的字节数,写完后成功返回;假设当前FIFO空暇缓冲区不可以容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写

     

    管道读端规则

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

    1.       假设有进程写打开FIFO。且当前FIFO内没有数据,将一直堵塞。

     

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

    2.       假设有进程写打开FIFO,且当前FIFO内没有数据。则返回-1,当前errno值为EAGAIN,提醒以后再试。

     

    管道读写规则代码举例

    写进程

    #include<unistd.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<fcntl.h>
    #include<limits.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include <errno.h>
    
    #define FIFO_NAME "/tmp/my_fifo"
    #define BUF_SIZE 88888
    int main(int argc,char *argv[])
    {
        int pipe_fd;
        int res;
        char buf[BUF_SIZE];
        memset(buf,3,BUF_SIZE);
        if(access(FIFO_NAME,F_OK)==-1)
        {
            res=mkfifo(FIFO_NAME,0766);
            if(res!=0)
            {
                fprintf(stderr,"不能创建管道文件 %s
    ",FIFO_NAME);
                exit(1);
            }
        }
    
        printf("进程PID %d 打开管道 O_WRONLY
    ",getpid());
        pipe_fd=open(FIFO_NAME,O_WRONLY|O_TRUNC);//1
       // pipe_fd=open(FIFO_NAME,O_WRONLY|O_TRUNC|O_NONBLOCK);//2
        if(pipe_fd!=-1)
        {
            res=write(pipe_fd,buf,sizeof(buf));
            printf("写入数据的长度是%d 
    ",res);
            close(pipe_fd);
        }
        else
            exit(1);
        exit(1);
    }
    

    读进程

    #include<unistd.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<fcntl.h>
    #include<limits.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    
    #define FIFO_NAME "/tmp/my_fifo"
    #define BUF_SIZE 4000
    
    int main(int argc, char *argv[]) {
    
    	int res;
        if(access(FIFO_NAME,F_OK)==-1)
        {
            res=mkfifo(FIFO_NAME,0766);
            if(res!=0)
            {
                fprintf(stderr,"不能创建管道文件 %s
    ",FIFO_NAME);
                exit(1);
            }
        }
    	char buf[BUF_SIZE];
    	memset(buf, 0, BUF_SIZE);
    	int pipe_fd;
     int num=0;
    	int bytes_read = 0;
    
    	printf("进程PID %d 打开管道 O_RDONLY
    ", getpid());
    	pipe_fd = open(FIFO_NAME, O_RDONLY);//3
    	//pipe_fd = open(FIFO_NAME, O_RDONLY|O_NONBLOCK);//4
    	if (pipe_fd != -1) {
    
    		do {
    			num++;
    			bytes_read = read(pipe_fd, buf, sizeof(buf));
    			printf("第%d次读入数据,数据的长度是%d 
    ",num, bytes_read);
    
    
    		} while (bytes_read != 0);
    
    		close(pipe_fd);
    	} else
    		exit(1);
    	exit(1);
    }
    


    上面两段代码各自是管道的读端进程与写端进程。当中有4个凝视行。分别代表

     

    1.       pipe_fd=open(FIFO_NAME,O_WRONLY|O_TRUNC);//1堵塞写端

    2.       pipe_fd= open (FIFO_NAME,O_WRONLY|O_TRUNC|O_NONBLOCK);//2非堵塞写端

    3.       pipe_fd =open(FIFO_NAME, O_RDONLY);//3堵塞读端

    4.       pipe_fd =open(FIFO_NAME, O_RDONLY|O_NONBLOCK);//4非堵塞读端

     

    能够分下面3种情况分析:

    说明:写端与读端不能同一时候都不堵塞

     

    写端堵塞。读端不堵塞

    控制台输出例如以下:

     

    读端进程:

    进程PID 5919打开管道 O_RDONLY

    1次读入数据。数据的长度是-1

    …………..

    5次读入数据,数据的长度是4000

    …………..

    26次读入数据,数据的长度是4000

    27次读入数据,数据的长度是888

    28次读入数据,数据的长度是0

     

    端进程:

    进程PID 5906打开管道 O_WRONLY

    写入数据的长度是88888

     

    分析:读端满足读端规则2。前面因为写进程还未開始写入数据到管道因此返回-1

    写端满足写端规则2

     

    写端不堵塞,读端堵塞

    运行流程:先运行读端程序,在运行写端

    控制台输出例如以下:

     

    读端进程:

    进程PID 6046打开管道 O_RDONLY

    1次读入数据。数据的长度是4000

    …………

    16次读入数据,数据的长度是4000

    17次读入数据,数据的长度是1536

    18次读入数据,数据的长度是0

     

    端进程:

    进程PID 6056打开管道 O_WRONLY

    写入数据的长度是65536

     

    分析:读端满足读端规则1,读进程在为读取到管道数据时一直处于等待堵塞状态

    写端满足写端规则3。写端写满管道后推出,因此写入数据长度是65535,而不是88888

      

    写端堵塞,读端堵塞

     

    控制台输出例如以下:

     

    读端进程:

    进程PID 8386打开管道 O_RDONLY

    1次读入数据。数据的长度是4000

    …………

    22次读入数据。数据的长度是4000

    23次读入数据,数据的长度是888

    24次读入数据,数据的长度是0

     

    端进程:

    进程PID 8373打开管道 O_WRONLY

    写入数据的长度是88888

     

    分析:读端满足读端规则1,读取处理用于读取数据已在管道等待堵塞状态

    写满足最终写的 - 侧规则2

  • 相关阅读:
    大家帮忙出几个招聘考试题目吧
    单元测试和设计模式在重构中的应用
    想起去年和女朋友第一次去吃饭的事情
    为什么我们常忘记使用正则表达式
    .NET实用设计模式:观察者模式(Observer)
    一个Outlook宏写的小程序,献给象我一样粗心大意的人
    单元测试应该测什么,不应该测什么?
    .NET实用设计模式:工厂模式(Factory)
    2021 系统架构设计师备考分享
    系统架构设计师论文企业集成
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5033023.html
Copyright © 2011-2022 走看看