Linux_Centos进程间通信_管道(匿名管道_命名管道)

Posted dodamce

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux_Centos进程间通信_管道(匿名管道_命名管道)相关的知识,希望对你有一定的参考价值。

1.管道是什么

两个进程通过打开同一个”文件“来实现进程间通信。
其中一个进程向文件里写入数据,另一个进程通过文件读取数据。
或者一个进程向文件里读取数据,另一个进程通过文件写入数据。
把从一个进程连接到另一个进程的一个这个“文件”称为一个管道

根据上面的分析可知,管道只能单向通信

2.匿名管道(父子进程通信)

①匿名管道原理

在我们创建子进程的时候,子进程会共享父进程的数据,当修改进程数据时发生写时拷贝。当父进程打开文件后再创建子进程,文件描述符信息也会被子进程接受到,两个进程通过文件描述符就可以找到同一个文件,就可以实现数据通信

②pipe函数 (unistd.h) 创建匿名管道

函数原型:
int pipe(int fd[2]);
参数意义:
fd为输出型参数,传入fd数组后,当函数调用结束后fd[0]表示读端,fd[1]表示写端。
fd数组中保存的是文件描述符
返回值:
成功返回0,失败返回-1

确认读写进程,关闭对应读写端

因为管道只能单向通信,所以当我们调用pipe后创建子进程后会出现下图状况

这时要确认读写进程,并且关闭对应的读写段。
eg:父进程要读取管道数据,此时关闭父进程的fd[1]。子进程要向管道写入数据,关闭子进程的fd[0].

父子进程匿名管道通信代码

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<stdlib.h>

int main()
{
  int fd[2]={0};
  if(pipe(fd)<0)
  {
    perror("pipe error\\n");
  }
  pid_t id=fork();
  if(id==0)
  {
    //child
    close(fd[0]);//子进程关闭读端
    const char* Str="Hello Linux";
    for(int i=0;i<10;i++)
    {
      write(fd[1],Str,strlen(Str));//向管道写入数据
      sleep(1);
    }
    close(fd[1]);
    exit(0);
  }
  //father
  close(fd[1]);
  char buff[32]={0};
  while(1)
  {
    ssize_t ReadSize=read(fd[0],buff,sizeof(buff));
    if(ReadSize>0)//读取到数据
    {
      buff[ReadSize]='\\0';//补充字符串接受的标准
      printf("receive:%s\\n",buff);
    }
    else if(ReadSize==0)//读取结束
    {
      printf("read end!\\n");
      break;
    }
    else //读取失败
    {
      perror("read error!\\n");
      break;
    }
  }
  waitpid(id,NULL,0);//父进程阻塞式等待
  return 0;
}

注意:
1.实现父子进程不可以通过设置全局变量的方式来实现,因为在创建子进程后,为了保持进程独立性,子进程在修改数据时会发生写时拷贝。此时父子进程的全局变量不是同一份了。

2.如果写端关闭,此时读端read会返回0代表文件结束

3.当管道文件为空时,此时读取进程挂起等待写入。当管道文件写满时,此时写入进程挂起等待读取。

4.当管道读取进程被关闭,此时管道写入进程会被操作系统终止,因为写入的数据无意义。

3.管道对临界资源的保护(同步与互斥)

在多执行流下,父子进程看到的同一份资源称为临界资源。

eg:
当子进程正在向管道中写入数据,父进程此时不能进入管道中读取数据。

理由:
管道内部提供了互斥与同步机制,管道文件为空时,此时父进程的read函数会被阻塞不再读取。

4.命名管道(任意两个进程通信)

命名管道有文件名,多个进程可以通过打开同一个文件,来让不同进程访问同一份资源。

①mkfifo函数(sys/type.h sys/stat.h)

函数原型
int mkfifo(const char*Pathname,mode_t mode);
参数解释:
Pathname:创建命名管道的路径和名称。
mode:创建命名管道的文件权限
返回值:创建成功返回0,失败返回-1。

②命名管道练习:利用管道实现进程A向进程B发送字符串

因为我们要让不同进程找到相同的管道文件,所以将两个进程所需要的文件名以及头文件放到Common.c中

Common.c

#pragma once

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

#define FIFE_NAME "MyFiFo"

server是创建管道文件,并且读取管道文件的数据,将这些数据打印到显示器上

#include"Commmen.c"

int main()
{
  if(mkfifo(FIFE_NAME,0664)<0)//创建管道文件
  {
    perror("make fifo error\\n");
    return 1;
  }

  int fd=open(FIFE_NAME,O_RDONLY);//打开管道文件,只读取
  if(fd<0)
  {
    perror("open error\\n");
    return 2;
  }
  char buff[32]={0};
  while(1)
  {
    buff[0]='\\0';//每次情况字符串
    ssize_t size=read(fd,buff,sizeof(buff)-1);//读取字符串放到buff缓冲区中,空余一个空间存放'\\0'
    if(size>0)//读取成功
    {
      buff[size]='\\0';//为字符串添加'\\0'
      printf("client:%s\\n",buff);
    }
    else if(size==0)//管道关闭
    {
      printf("End Client\\n");
      break;
    }
    else //读取失败
    {
      perror("read error\\n");
      break;
    }
  }

  close(fd);
  return 0;
}

client是读取键盘上的字符,并且将这些字符写到管道文件中。
client.c

#include"Commmen.c"

int main()
{
  int fd=open(FIFE_NAME,O_WRONLY);//通过写的方式打开管道文件
  if(fd<0)
  {
    perror("open error\\n");
    return 1;
  }
  char buff[32]={0};
  while(1)
  {
    printf("Enter String\\n");
    buff[0]='\\0';//清空字符缓冲区
    ssize_t size=read(0,buff,sizeof(buff));//0为标准输入的文件描述符,读取键盘输入的字符
    if(size>0)
    {
      buff[size]='\\0';
      write(fd,buff,strlen(buff)-1);//向管道文件中写入buff数组的数据
    }
    else 
    {
      printf("error wirte\\n");
    }
  }
  close(fd);
  return 0;
}

运行结果:

注意:当我们向管道文件写入而不读取时,管道文件的大小并不会发生变化,这些数据保存在系统中,没有刷新到磁盘中。双方通信在内存中通信

以上是关于Linux_Centos进程间通信_管道(匿名管道_命名管道)的主要内容,如果未能解决你的问题,请参考以下文章

Linux系统编程--进程间通信 ---管道篇

Linux进程间通信

Linux进程间通信

Linux进程间通信

实验八进程间通信

进程间通信之管道