Linux进程通信之管道(匿名,命名)

来源:互联网 发布:炫浪网络社区下载不了 编辑:程序博客网 时间:2024/06/07 16:14

进程间通信产生的条件:

  每个进程各⾃自有不同的⽤用户地址空间,任何⼀一个进程的全局变量在另⼀一个进程中都看不到 所以进 程之间要交换数据必须通过内核,在内核中开辟⼀一块缓冲区,进程1把数据从⽤用户空间 拷到内核缓 冲区,进程2再从内核缓冲区把数据读⾛走,内核提供的这种机制称为进程间通信 (IPC,InterProcess Com)
  管道就是进程间通信最基本的一种通信方式。
  管道是一种IPC机制,由pipe函数创建:
  

#include <unistd.h  >int pipe(pipefd[2]);

  管道由父进程创建,得到两个文件描述符指向管道的两端。再由父进程创建子进程,此时,子进程继承了父进程的文件描述符表,管道算作一个公共资源,这样父子进程都可以看到这块空间,可以对这块空间进行读写操作。
  管道是由pipe函数在系统内核中开辟的一块公共空间,两个进程均可访问这快空间,即形成了进程间的通信。简而言之,进程通信就是一块公共资源,不同的进程选择的读写方式不同,就造成了进程之间的通信。管道有两个端口(可以想象成现实生活中的管子),一个端口为读端口,另一个端口是写端口,管道的读写端通过一个只有两个元素的数组确定,pipe函数的参数pipefd[2],其中pipefd[0]为读端,pipefd[1]位写端。
  其实,pipefd的两个成员就相当于文件描述符表中的stdout和stdin。如图:
  这里写图片描述
 

匿名管道:

  但是,进程间使用匿名管道通信的缺点也很明显:
  1、只能在有血缘关系的进程间通信;
  2、管道的具有单向导通性;
  关于第一点,很好理解,即:父进程创建了一个子进程,那么这两个进程就具有血缘关系,所以可以使用匿名管道通信,或者说子进程又创建了一个孙子进程,那么父进程和孙子进程也可以进行基于匿名管道的通信。
  第二个:管道的单向导通性,这个性质在稍后讲的命名管道也存在。即:若是已经决定由父进程向子进程发送数据,那么之后就不可更改,只能由父进程发,子进程收,期间不肯可能由子进程发,父进程收可能性存在。
  这里写图片描述

管道的自带互斥同步机制

  
  假设:子进程向父进程发送数据。
  有没有存在这样一个情况,子进程准备发送的数据为:”hello,father!”,但是在写完hello或者正在写,father的时候,父进程来读取数据,只将hello读走,或读走了一些无效数据,这不是就造成了通信的错误吗?
  所以管道自带了互斥与同步机制,写端在写数据且没有写完时,读端是不可能访问管道这块公共资源的,就好像有一个守门人在看着这块公共资源,有一个人进去了,另一个人只能等着。
  
  

匿名管道的五大性质:

  1、单向导通性;
  2、只能用于存在血缘关系的进程间通信;
  3、面向数据流;
  4、管道的生命周期为“随进程”;
  5、管道为空时,则停止读取。
  
  何为管道的生命周期“随进程”?很好理解,即:进程结束,管道消失。
  那为什么管道为空时,要强制停止读取,因为一直在读取的操作浪费了系统的资源,系统需要停止这一个无用的操作。

  简单的代码实现:
  

#include <stdio.h>#include <unistd.h>#include <string.h>int main(){    int fp[2];  //一组文件描述符    //注意:管道的创建一定要在子进程创建之前    int p = pipe(fp);//创建管道    char buff[32];    int i=10;    pid_t id = fork();    if(p<0)    {        perror("pipe");        return -1;    }    if(0 == id)//子进程    {        close(fp[0]);//关闭子进程的读端,只让子进程写        char str[]="hello,world!";        while((i--)>0)//循环10次        {            write(fp[1],str,strlen(str));            printf("child write: %s\n",str);            sleep(1);        }    }    else//父进程    {        sleep(1);        close(fp[1]);//关闭父进程的写端,只允许父进程读        int a = 0;        while((i--)>0)        {            ssize_t s = read(fp[0],buff,sizeof(buff));            buff[s-1]=0;//读了s个数据,在末尾加上'\0',使输出时只输出写的数据            printf("father read: %d %s\n",a++,buff);            sleep(1);        }    }    return 0;}

这里写图片描述

命名管道

  命名管道的性质与匿名管道的性质相同,只有几点不同,但是正是这几点才是二者的却别所在。

命名管道的性质

相同点:
  1、单向导通性——只要是管道,就摆脱不了这个性质;
  2、面向数据流通信;
  3、管道为空,则停止读取数据;
不同点:
  1、生命周期“随内核”;
  2、可以进行不同进程间的通信,不局限于只有血缘关系的进程。

  命名管道的创建使用mkfifo函数
  

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

  pathname表示管道的名称或路径,mode表示管道的权限,跟umask的设置相同。
  地爱用此函数就会创建一个命名管道,可以在调用此函数后返回创建管道的路径下面搜索,就会发现一个管道文件,这个现象就表明了命名管道的声明周期是随内核的。期间除非手动删除,否则不会消失。
  这里写图片描述
  此时需要编写两个文件,即表示两个毫不相干的进程,一个使用写端写数据,另一个使用读端读数据。
  创建管道完成后,使用open函数打开管道,使用相应的”O_WDONLY”,”O_WRONLY”来标明创建的读写方式。
  代码实现:
  

server.c//创建管道并发送数据#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main(){    char* pipe_name="./myfifo";//管道名称    int open_code=O_WRONLY;//打开管道的放式为只写    char buff[1024];    if(access(pipe_name,F_OK)<0)//判断管道是否已经存在,不存在则创建    {        int fifoid = mkfifo(pipe_name,0666);        if(fifoid<0)        {            perror("mkfifo");            return -1;        }    }    int pipefd = open(pipe_name,open_code);//打开管道    if(pipefd<0)    {        perror("open");        return -2;    }    while(1)    {        printf("SERVER TO CLIENT------->WRITE\n");        ssize_t s = read(0,buff,1024);//从标准输入读取数据,即读取键盘的输入作为写入管道的数据        buff[s]=0;        write(pipefd,buff,1024);    }    close(pipefd);//关闭管道    return 0;}client.c//读数据#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main(){    //管道在server.c已经创建好,不用再次创建,只需打开就行。    char* pipe_name="myfifo";    int open_mode=O_RDONLY;//打开方式为只读    char buff[1024];    int pipeid=open(pipe_name,open_mode);//打开管道    if(pipeid<0)    {        perror("open");        return -1;    }    while(1)    {        ssize_t s=read(pipeid,buff,1024);        buff[s]=0;        if(s=0)        {            printf("no one send something to you\n");            return -3;        }        printf("erver to lient------->read:%s\n",buff);    }    return 0;}

运行截图:

server.c写端:

这里写图片描述

  client.c读端:
这里写图片描述

  若文中有不准确或不正确之处,请指出,谢谢!

原创粉丝点击