Linux进程间通信——管道(pipe)与命名管道(FIFO)
2017-05-30 12:34
537 查看
1.进程间通信
问题:每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个近程中都看不到。解决方案:进程之间要交换数据必须通过内核,因此在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走。
内核提供的这种机制称为进程间通信(IPC ,Inter Process Communication).
本质:让不同的进程看到一份公共的资源,这份资源由内核提供。
2.管道(pipe)
管道是一种最基本的IPC机制,具有以下特征: 1. 只能进行单向通信; 2. 只适合于有血缘关系之间的进程通信; 3. 通信时,写端写一条,读端读一条,自带同步机制; 4. 通信时是面向字节流的服务; 5. 当与之相关的进程退出时,管道也随之关闭。这说明管道的生命周期随进程。
2.1 管道的创建
由pipe函数创建
#include<unistd.h> int pipe(int filedes[2]);
该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。
2.2 管道间的通信
1.父进程调用pipe开辟管道,得到两个文件描述符指向管道的两段;
2.父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道;
3.父进程关闭管道读端fd[0],子进程关闭管道写端fd[1]。这样就实现了进程通信。
2.3 测试代码
3. 命名管道(FIFO)
3.1 什么是命名管道?命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的没有名字的管道(匿名管道)类似。
3.2 创建命名管道
我们可以使用两下函数之一来创建一个命名管道,他们的原型如下:
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *filename, mode_t mode); int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);
注意:filename指定了文件名,而mode则指定了文件的读写权限。
3.3 访问管道
使用open()函数打开FIFO文件,通常有四种方式
open(const char *path, O_RDONLY);//1 open(const char *path, O_RDONLY | O_NONBLOCK);//2 open(const char *path, O_WRONLY);//3 open(const char *path, O_WRONLY | O_NONBLOCK);//4 O_RDONLY:只读方式 O_NONBLOCK:打开(open) 文件可以以非块(non-blocking) 模式打开 . 此时并没有打开 , 也不能使用返回的文件描述符进行后续操作 , 而是使调用程序等待 . 此模式是为了FIFO (命名管道) 的处理 , 这种模式对除了 FIFO 外没有任何影响 .(非阻塞) O_WRONLY:只写方式
open()调用的阻塞
对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。
对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。
3.4 使用FIFO实现进程间的通信
FIFO write端
#include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <limits.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <string.h> int main() { const char *fifo_name = "/tmp/fifowrite"; int pipe_fd = -1; int data_fd = -1; int res = 0; const int open_mode = O_WRONLY; int bytes_sent = 0; char buffer[PIPE_BUF + 1]; if(access(fifo_name, F_OK) == -1) { //管道文件不存在 //创建命名管道 res = mkfifo(fifo_name, 0777); if(res != 0) { fprintf(stderr, "Could not create fifo %s\n", fifo_name); exit(EXIT_FAILURE); } } printf("Process %d opening FIFO O_WRONLY\n", getpid()); //以只写阻塞方式打开FIFO文件,以只读方式打开数据文件 pipe_fd = open(fifo_name, open_mode); data_fd = open("Data.txt", O_RDONLY); printf("Process %d result %d\n", getpid(), pipe_fd); if(pipe_fd != -1) { int bytes_read = 0; //向数据文件读取数据 bytes_read = read(data_fd, buffer, PIPE_BUF); buffer[bytes_read] = '\0'; while(bytes_read > 0) { //向FIFO文件写数据 res = write(pipe_fd, buffer, bytes_read); if(res == -1) { fprintf(stderr, "Write error on pipe\n"); exit(EXIT_FAILURE); } //累加写的字节数,并继续读取数据 bytes_sent += res; bytes_read = read(data_fd, buffer, PIPE_BUF); buffer[bytes_read] = '\0'; } close(pipe_fd); close(data_fd); } else exit(EXIT_FAILURE); printf("Process %d finished\n", getpid()); exit(EXIT_SUCCESS); }
FIFO read端
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <limits.h> #include <string.h> int main() { const char *fifo_name = "/tmp/fiforead"; int pipe_fd = -1; int data_fd = -1; int res = 0; int open_mode = O_RDONLY; char buffer[PIPE_BUF + 1]; int bytes_read = 0; int bytes_write = 0; //清空缓冲数组 memset(buffer, '\0', sizeof(buffer)); printf("Process %d opening FIFO O_RDONLY\n", getpid()); //以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名 pipe_fd = open(fifo_name, open_mode); //以只写方式创建保存数据的文件 data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644); printf("Process %d result %d\n",getpid(), pipe_fd); if(pipe_fd != -1) { do { //读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中 res = read(pipe_fd, buffer, PIPE_BUF); bytes_write = write(data_fd, buffer, res); bytes_read += res; }while(res > 0); close(pipe_fd); close(data_fd); } else exit(EXIT_FAILURE); printf("Process %d finished, %d bytes read\n", getpid(), bytes_read); exit(EXIT_SUCCESS); }
4 总结
pipe与FIFO的区别:1.管道只适用于具有血缘关系之间的进程通信,而命名管道则突破这一限制。可以适用于毫无相关的进程之间通信。
相关文章推荐
- Linux环境进程间通信(一)——管道(pipe)和命名管道(fifo)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux环境进程间通信(一)——管道(pipe)和命名管道(fifo)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux环境进程间通信(一)——管道(pipe)和命名管道(fifo)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
- Linux进程间通信之命名管道(FIFO)
- Linux--进程间通信(二)-命名管道(pipe)通信
- Linux进程间通信(IPC)之二——命名管道(FIFO)
- Linux下进程间通信机制:FIFO(命名管道)
- Linux下进程间通信机制:FIFO(命名管道)
- 进程间通信1—管道(pipe)和命名管道(FIFO)
- Linux进程间通信(IPC)之二——命名管道(FIFO)
- 【Linux】进程间通信-命名管道FIFO