Linux_进程间通信之匿名管道

进程间通信

目的:

  • 数据传输:一个进程将数据发给另一个进程
  • 资源共享:多进程共享同样的资源
  • 通知事件:一个进程向另一个进程通知某事件发生,如进程终止要通知父进程
  • 进程控制:有些进程希望完全控制另一个进程的执行,如Debug进程,此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道他的状态改变

分类:

  • 管道:匿名管道pipe、命名管道
  • System V IPC:消息队列、共享内存、信号量
  • POSIX IPC:消息队列、共享内存、信号量、互斥量、条件变量、读写锁

管道:把一个进程连接到另一个进程的一个数据流称为管道

匿名管道

#include <unistd.h>
//创建一个无名管道
int pipe(int pipefd[2]);
//pipefd:文件描述附数组;pipefd[0]表示读端,pipefd[1]表示写端
//成功返回0;失败返回错误代码

调用pipe函数的进程(用户):
pipefd[1] —w---> 管道(内核) —r---> pipefd[0]

#include<iostream>
#include<stdio.h>
#include<unistd.h>
#include<cstring>
#include<cstdlib>

using namespace std;

int main()
{
	char buffer[128];
	int pipefds[2];
	int flag = pipe(pipefds);
	if(flag == -1)
	{
		perror("make pipe");
		_exit(1);
	}
	//读取键盘输入写入buffer
	while(fgets(buffer, sizeof(buffer), stdin))
	{
		int len = strlen(buffer);
		//将buffer内容写入管道
		if(write(pipefds[1], buffer, len) != len)
		{
			perror("write to pipe");
			break;
		}
		//把buffer内容置空
		memset(buffer, 0x00, sizeof(buffer));
		//从管道读的内容写入buffer
		if((len = read(pipefds[0], buffer, sizeof(buffer))) == -1)
		{
			perror("read from pipe");
			break;
		}
		//将buffer内容写入显示器
		if(write(1, buffer, len) != len)
		{
			perror("write to stdout");
			break;
		}
	}
	return 0;
}

用fork来共享管道原理
在这里插入图片描述
fork之后各自关掉不用的描述附,即关掉父进程fd[1]和子进程fd[0]

文件描述附角度理解管道
在这里插入图片描述

  • 图为一个父进程创建管道。
  • 若父进程fork出子进程,则子进程fd也连接到管道两端。
  • 父进程关闭fd[0],子进程关闭fd[1]时,父进程只写,子进程只读。

内核角度理解管道
在这里插入图片描述

#include<iostream>
#include<stdio.h>
#include<unistd.h>
#include<cstdlib>
#include<error.h>
#include<cstring>

#define ERR_EXIT(m) do{perror(m);exit(EXIT_FAILURE);}while(0)

using namespace std;

int main()
{
	int pipefd[2];
	if(pipe(pipefd) == -1)
	{
		ERR_EXIT("pipe error");
	}
	pid_t pid = fork();
	if(pid == -1)
	{
		ERR_EXIT("fork error");
	}
	if(pid == 0)
	{
		close(pipefd[0]);
		write(pipefd[1], "hello", 5);
		close(pipefd[1]);
		exit(EXIT_SUCCESS);
	}
	close(pipefd[1]);
	char buf[10] = {};
	read(pipefd[0], buf, 10);
	cout<<buf<<endl;
	close(pipefd[0]);
	return 0;
}

管道读写规则

  • 没有数据可读时
    O_NONBLOCK disable:read调用阻塞,进程暂停执行,一直等到有数据来
    O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN
  • 当管道满的时候
    O_NONBLOCK disable:write调用阻塞,直到有进程读走数据
    O_NONBLOCK enable:write调用返回-1,errno值为EAGAIN
  • 如果所有管道写端对应文件描述附被关闭,则read返回0
  • 如果所有管道读端对应文件描述附被关闭,则write操作会产生信号SIGPIPE,可能导致write进程退出
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性
  • 当要写入的数据量大于PIPE_BUF时,linux将不保证写入的原子性

管道的特点

扫描二维码关注公众号,回复: 11098957 查看本文章
  • 只能用于具有共同祖先的进程间通信。通常一个管道由一个进程创建,然后改进程调用fork,此后父子进程就可用该管道
  • 管道提供流式服务
  • 一般的,进程退出,管道释放
  • 一般的,内核会对管道操作进行同步与互斥
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信只能建立两个管道
    在这里插入图片描述
发布了72 篇原创文章 · 获赞 25 · 访问量 7337

猜你喜欢

转载自blog.csdn.net/qq_41245381/article/details/103959460