linux多进/线程编程(4)——进程间通信之pipe和fifo
前言:
Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。
常用的进程间通信方式有:
① 管道 (使用最简单)
② 信号 (开销最小)
③ 共享映射区 (无血缘关系)
④ 本地套接字 (最稳定) https://zhuanlan.zhihu.com/p/336734605
本文主要讲解pipe管道的用法和代码示例。
管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
1. 其本质是一个伪文件(实为内核缓冲区)
2. 由两个文件描述符引用,一个表示读端,一个表示写端。
3. 规定数据从管道的写端流入管道,从读端流出。
管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
管道的局限性:
1. 数据一旦被读走,便不在管道中存在,不可反复读取。
2. 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
3. 只能在有公共祖先的进程间使用管道。
pipe
NAME pipe, pipe2 - create pipe SYNOPSIS #include <unistd.h> int pipe(int pipefd[2]); #define _GNU_SOURCE /* See feature_test_macros(7) */ #include <fcntl.h> /* Obtain O_* constant definitions */ #include <unistd.h> int pipe2(int pipefd[2], int flags);
利用pipe实现ps aux | grep bash
int pipe_fd[2]; pipe(pipe_fd); pid_t pid = fork(); if (pid == 0) { //son -----> 这里会产生僵尸进程 //管道的使用规范:关闭读端 close(pipe_fd[0]); //1.先重定向 dup2(pipe_fd[1], STDOUT_FILENO);//标准输出重定向到管道写端 //2.execlp execlp("ps", "ps", "aux", nullptr); } else if (pid > 0) { //parent //管道的使用规范:关闭写端 close(pipe_fd[1]); //1.先重定向 dup2(pipe_fd[0], STDIN_FILENO);//标准输入重定向到管道读端 //2.execlp -----> grep 会阻塞 execlp("grep", "grep", "--color=auto", "bash", nullptr); //代码的问题:父进程认为还有写端存在,就有可能还有人给发数据,继续等待 //管道的使用规范 }
fifo 可以实现无血缘关系的进程间通信
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> int main(int argc, char * argv[]) { if (argc != 2) { printf("./a.out fifonname\n"); return -1; } //当前目录有一个myfifo文件 //打开fifo文件 int fd = open(argv[1], O_WRONLY); //写 char buf[256]; int num = 1; while(1) { memset(buf, 0x00, sizeof(buf)); //循环写 sprintf(buf, "xiaoming%04d", num++); write(fd, buf, strlen(buf)); sleep(1); } //关闭描述符 closde(fd); return 0; }
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> int main(int argc, char * argv[]) { if (argc != 2) { printf("./a.out fifonname\n"); return -1; } //当前目录有一个myfifo文件 //打开fifo文件 int fd = open(argv[1], O_RDONLY); char buf[256]; int ret = 0; while(1) { //循环读 ret = read(fd, buf, sizeof(buf)); if (ret > 0) { printf("read:%s\n", buf); } } //关闭描述符 closde(fd); return 0; }