IPC(一)—— 匿名管道 pipe

IPC 方法

Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。
常用的进程间通信的方式有:

  • 管道 (使用最简单)
  • 信号 (开销最小)
  • 共享映射区 (无血缘关系)
  • 本地套接字 (最稳定)

管道

管道是一种最基本的 IPC 机制,作用于有血缘关系的进程之间,完成数据传递。调用 pipe 系统函数即可创建一个管道。
【管道的原理】: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
【管道的特征】:

  • 其本质是一个伪文件(实为内核缓冲区);
  • 由两个文件描述符引用,一个表示读端,一个表示写端;
  • 规定数据从管道的写端流入管道,从读端流出;

【管道的局限性】:

  • 数据自己读不能自己写;
  • 数据一旦被读走,便不在管道中存在,不可反复读取;
  • 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动,双向通信需建立两个管道;
  • 只能在有公共祖先(父子、兄弟进程)的进程间使用管道;

pipe

创建管道。

  • SYNOPSIS
    #include 
    
    int pipe(int pipefd[2]);
    
  • DESCRIPTION
    • pipefd[2]:传出参数,函数调用成功返回 r/w 两个文件描述符;规定:fd[0] → r; fd[1] → w。无需open,但需手动close。向管道文件读写数据其实是在读写内核缓冲区。
  • RETURN VALUE
    成功:0;失败:-1,设置errno。
  • USAGE
    管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。父子进程间的通信通常可以采用如下步骤。
    IPC(一)—— 匿名管道 pipe_第1张图片
  1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端;
  2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道;
  3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信;

管道的读写行为

  • 读管道:
    • 管道中有数据:read返回实际读到的字节数;
    • 管道中无数据:
      • 管道写端被全部关闭,read返回0 (好像读到文件结尾);
      • 写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)
  • 写管道:
    • 管道读端全部被关闭:进程异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)
    • 管道读端没有全部关闭:
      • 管道已满,write阻塞;
      • 管道未满,write将数据写入,并返回实际写入的字节数;

管道缓冲区大小

使用ulimit –a 命令来查看当前系统中创建管道文件所对应的内核缓冲区大小,通常为4K大小;

示例

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
	pid_t pid;
	int pipefd[2] = {0};
	char buf[1024] = {0};
	char *str = "hello pipe\n";
	int len;

	if (-1 == pipe(pipefd))
		perror("pipe");

	pid = fork();
	if (-1 == pid) {
		perror("fork error");
	} else if (pid == 0) {
		close(pipefd[1]);
		len = read(pipefd[0], buf, sizeof(buf));
		write(STDOUT_FILENO, buf, len);
		
	} else {
		close(pipefd[0]);
		write(pipefd[1], str, strlen(str));
		wait(NULL);
	}

	return 0;
}

你可能感兴趣的:(#,IPC,linux)