Linux进程通信之管道
进程通信之管道
管道其实就是一个特殊的文件。管道通信是半双工通信,即数据的流动是单向流动的,大致分为匿名管道和命名管道。
匿名管道,是一个只存在于内存中,不存在于文件系统中,并且只能在具有亲缘关系的进程之间使用,即父子进程和兄弟进程。一个进程在管道的一段写数据,一个进程在管道的另一边读数据,这就是匿名管道。
命名管道,是一个不止存在于内存中,在文件系统上有自己名字的一个文件,任何进程任何时间都能通过文件名和路径去使用这个管道,但是数据还是存储在内存中。
匿名管道的实现
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<stdlib.h>int main(int argc, char const *argv[])
{int fd[2];pid_t pid;char buffer[128];if(pipe(fd) < 0)printf("creat pipe faild\n");pid = fork();if(pid < 0){printf("create fork fail\n");}else if (pid > 0){//父进程sleep(3);printf("this is father,pid = %d\n", getpid());close(fd[0]);write(fd[1], "hello highgo", strlen("hello highgo"));wait(NULL);}else {//子进程printf("this is child,pid = %d, ppid = %d\n",getpid(),getppid());close(fd[1]);read(fd[0],buffer,sizeof(buffer));printf("%s\n",buffer);}return 0;
}pipe_test.c
在上述代码中,fd[2]就是匿名管道,fd[0]是读的区域,fd[1]是写的区域。
如果内容在写之前读取,则该进程将会被挂起。
文件缓冲区是有限的,可以通过命令查看
cat /proc/sys/fs/file-max
可以通过ulimit进行修改
深入了解Linux管道缓冲大小设置 (linux pipe 缓冲大小) – 后浪云 (idc.net)
命名管道
命名管道的实现分为两个文件,这两个文件分别为两个进程。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <limits.h>#ifndef PIPE_BUF
#define PIPE_BUF 4192
#endifint main(int argc, char const *argv[])
{const char* fifoname = "my_fifo";int pipe_fd = -1;int data_fd = -1;int res = 0;char pipe_buffer[PIPE_BUF + 1] = {0};int bytes_read = 0;int bytes_sent = 0;//检查文件是否已经存在if(access(fifoname,F_OK) == -1){res = mkfifo(fifoname, 0600);if(res != 0){printf("create fifo faild");exit(EXIT_FAILURE);}} //需要往管道里面写东西,所以以读写的方式打开pipe_fd = open(fifoname, O_WRONLY);//只需要读取data.txt文件里面的东西,不需要写入,所以只读的方式打开data_fd = open("data.txt", O_RDONLY);if (pipe_fd != -1){//获取写入管道的字节大小bytes_read = read(data_fd, pipe_buffer, PIPE_BUF);//将读取的数据的最后一位设置为字符串的结束符号pipe_buffer[bytes_read] = '\0';while (bytes_read > 0){/* 将读取到缓冲区的数据写入到管道中去 */res = write(pipe_fd, pipe_buffer, bytes_read);if(res == -1){printf("write error on pipe\n");exit(EXIT_FAILURE);}//记录读取的到的所有字节数大小bytes_sent += res;bytes_read = read(data_fd, pipe_buffer, PIPE_BUF);pipe_buffer[bytes_read] = '\0';}//关闭释放资源close(data_fd);close(pipe_fd);}else{close(data_fd);perror("fifofile open error:");exit(EXIT_FAILURE);}printf("read finished,have sent %d bytes,pid = %d", bytes_sent, getpid());return 0;
}mkfifo_w.c
这个进程会将当前已经存在的一个data.txt文件打开,读取其中的数据,然后将其写入管道my_fifo中,供其他进程进行读取,并且循环读取该文件并写入管道。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <limits.h>#ifndef PIPE_BUF
#define PIPE_BUF 4192
#endifint main(int argc, char const *argv[])
{const char *fifoname = "my_fifo";int pipe_fd = -1;int data_fd = -1;int res = 0;char pipe_buffer[PIPE_BUF + 1] = {0};int bytes_read = 0;int bytes_write = 0;//打开管道文件,以读写的权限,这里其实以读的权限即可pipe_fd = open(fifoname, 0600);//需要将读取的信息写入该文件,故赋予读写的权限还有创建的权限data_fd = open("data_fuben.txt", O_WRONLY|O_CREAT, 0644);if(pipe_fd != -1){do{res = read(pipe_fd, pipe_buffer, PIPE_BUF);bytes_write = write(data_fd, pipe_buffer, res);bytes_read += res;}while(res > 0);close(data_fd);close(pipe_fd);}else{close(data_fd);perror("fifofile open error:");exit(EXIT_FAILURE);}printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);return 0;
}mkfifo_w.c
匿名管道和命名管道差异
不同之处:
- 可见性与持久性:命名管道(FIFO)在文件系统中可见,并作为特殊文件存在,即使创建进程退出后仍然保留,以便后续使用。而管道(pipe)则不可见,且在使用它的进程退出后自动消失。
- 通信范围:命名管道(FIFO)允许不相关的进程通过打开管道文件进行通信,而管道(pipe)通常用于具有亲缘关系的进程之间的通信,如父子进程。
- 缓冲区大小:管道(pipe)的缓冲区大小在不同系统中可能有所不同,而命名管道(FIFO)作为文件系统中的特殊文件,其缓冲区大小可能受到文件系统的限制。
- 有名与无名:命名管道(FIFO)有具体的名称,可以通过名称来引用和访问;而管道(pipe)通常是匿名的,没有具体的名称标识。