进程间通信 管道

在Linux中,管道是一种通信机制,用于将一个程序的输出直接连接到另一个程序的输入。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,它可以克服使用文件进行通信的两个问题,具体表现为限制管道的大小和读取进程可能工作得比写进程快。管道的思想是在内存中创建一个共享文件,从而使通信双方利用这个共享文件来传递信息。由于这种方式具有单向传递数据的特点,所以这个作为传递消息的共享文件就叫做“管道”。在管道的具体实现中,根据通信所使用的的文件是否具有名称,有“匿名管道”和“命名管道”。

匿名管道是一种具有亲缘关系的进程间的通信机制,它由创建它的进程及其子孙进程使用。匿名管道由pipe函数创建并打开。

命名管道克服了匿名管道只能在具有亲缘关系进程间通信的缺点,使不具有亲缘关系的进程间也能通过命名管道进行通信。命名管道由mkfifo函数创建。命名管道在文件系统中有对应的文件名。命名管道通过打开该文件名来打开管道,从而实现与其他进程的通信。

管道是连接两个进程的连接器(特殊文件)

匿名管道

当一个管道建立后,将获得两个文件描述符,分别用于对管道读取写入,通常将其称为管道的读端写端

image-20231202182312664

image-20231202182327485

所用到的函数:

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

参数说明:

pip数组:存放两个文件描述符

  • pipefd[0]:存放管道读端文件描述符。
  • pipefd[1]:存放管道写端文件描述符。

返回值:

  • 调用成功,返回0
  • 调用失败,返回-

管道的读写操作

读规则
  1. 关闭管道的写端
close(fd[1]);
  1. 从管道读端fd[0]读出size个字符放到buf
read(fd[0], buf, size);
  1. 读完关闭管道的读端
close(fd[0]);
写规则
  1. 关闭管道的读端
close(fd[0]);
  1. buf中的长度为size的字符送到写端fd[1]
write(fd[1], buf, size);
  1. 写完关闭管道的写端
close(fd[1]);

例子:在父进程向管道内写入"Hello World\n",之后在子进程中读取内容。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int main()
{int fd[2];int ret = pipe(fd);char buf[100] = "Hello Wrold\n";if(ret == -1) {perror("pipe error");exit(1);}pid_t pd = fork();if(pd > 0) {// 关闭读端close(fd[0]);// 用写端,写入内容write(fd[1], buf, sizeof(buf));close(fd[1]);wait(NULL);} else if(pd == 0) {// 关闭写端close(fd[1]);// 从读端读取内容read(fd[0], buf, sizeof(buf));close(fd[0]);write(STDOUT_FILENO, buf, sizeof(buf));close(fd[0]);}
}

命名管道

命名管道与匿名管道的区别:

  • 匿名管道位于内存,只能用于有亲缘关系的进程通信
  • 命名管道位于文件系统中,可以实现不同进程之间的通信
  • 命名管道是一种双向通信管道,可以移动读/写模式打开

所用到的函数:

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

参数说明:

  • pathname:创建的FIFO文件名
  • mode:规定FIFO文件的读写权限

返回值:

  • 成功时,返回0
  • 失败时,返回-1

创建一个叫np的命名管道

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main()
{if(mkfifo("np", 0644) < 0) {perror("Error!");exit(1);}return 0;
}

向管道写入内容:

#include <stdio.h>  
#include <stdlib.h>  
#include <fcntl.h>  
#include <unistd.h>  int main() {  pid_t fd;  // 指定文件的完整路径,如果文件不存在,将会创建  if ((fd = open("np", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {  perror("error");  exit(-1);  }  printf("%d\n", fd);  char buf[]  = "Hello World\n";  write(fd, buf, sizeof(buf));  close(fd);  printf("write success\n");  return 0;  
}

读出内容:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{pid_t fd;char buf[100];if( (fd = open("np", O_RDONLY)) < 0) {perror("Error\n");exit(-1);}while( (read(fd, buf, sizeof(buf))) > 0) {write(STDOUT_FILENO, buf, sizeof(buf));}return 0;
}