首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【linux学习指南】 进程间通信&&匿名管道&&理解管道的本质

【linux学习指南】 进程间通信&&匿名管道&&理解管道的本质

作者头像
学习起来吧
发布2024-11-18 08:20:13
发布2024-11-18 08:20:13
4980
举报
文章被收录于专栏:学习C/++学习C/++

📝进程间通信介绍

🌉进程间通信⽬的

  • 数据传输:⼀个进程需要将它的数据发送给另⼀个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:⼀个进程需要向另⼀个或⼀组进程发送消息,通知它(它们)发⽣了某种事件(如进程终⽌时要通知⽗进程)。
  • 进程控制:有些进程希望完全控制另⼀个进程的执⾏(如Debug进程),此时控制进程希望能够拦截另⼀个进程的所有陷⼊和异常,并能够及时知道它的状态改变。

🌉进程间通信发展

  1. 管道(Pipe):
    • 管道是最早的进程间通信机制之一。
    • 管道允许一个进程向另一个进程写入数据,另一个进程可以读取数据。
    • 管道是单向的,数据只能在一个方向上流动。
  2. SystemV进程间通信:
    • 这是一种更加丰富和灵活的进程间通信机制。
    • 包括:
      • 消息队列 - 进程可以发送和接收消息
      • 共享内存 - 进程可以共享内存区域
      • 信号量 - 进程可以使用信号量进行同步
  3. POSIX进程间通信:
    • 这是一种标准化的进程间通信机制。
    • 包括:
      • 消息队列 - 类似于System V的消息队列
      • 共享内存 - 类似于System V的共享内存
      • 信号量 - 类似于System V的信号量
      • 管道和命名管道 - 类似于早期的管道机制
    • POSIX机制提供了更好的可移植性和标准化。

🌉进程间通信分类

  • 管道 匿名管道pipe 命名管道
  • SystemVIPC SystemV消息队列 SystemV共享内存 SystemV信号量
  • POSIXIPC 消息队列 共享内存 信号量 互斥量 条件变量 读写锁

🌠管道

什么是管道

  • 管道是Unix中最古⽼的进程间通信的形式。
  • 我们把从⼀个进程连接到另⼀个进程的⼀个数据流称为⼀个“管道”

🌉匿名管道

代码语言:javascript
复制
#include <unistd.h>
功能:创建⼀⽆名管道
原型
int pipe(int fd[2]);
参数
fd:⽂件描述符数组,其中fd[0]表⽰读端, fd[1]表⽰写端
返回值:成功返回0,失败返回错误代码

🌉实例代码

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(void)
{
	int fds[2];
	char buf[100];
	int len;

	//使用pipe(fds),如果成功,则会将fds[0].fds[1]分别写入R,W文件描述符
	if( pipe(fds) == -1)
		perror("make pipe"), exit(1);

	while(fgets(buf,100,stdin))
	{
		len = strlen(buf);

		if(write(fds[1], buf, len) != len)
		{
			perror("write to pipe");
			break;
		}

		memset(buf, 0x00, sizeof(buf));
		//将buf数组中的所有100个字节全部初始化为0x00,也就是10进制的0,目的是为了清空内存区域。

		//read from pipe
		if(((len = read(fds[0],buf,100)) == -1))
		{
			perror("read from pipe");
			break;
		}

		//write to stdout
		if(write(1, buf, len) != len)
		{
			perror("write to stdout");
			break;
		}
		

	}
	return 0;
}

🌠三种理解管道方法

🌉⽤fork来共享管道原理

在 Unix/Linux 系统中,我们可以使用 fork() 系统调用来创建子进程,并通过共享管道(pipe)进行进程间通信。下面是使用 fork() 来共享管道的原理:

首先,父进程创建一个管道:

代码语言:javascript
复制
int fds[2];
pipe(fds);

这将创建一个管道,其读端文件描述符存储在 fds[0],写端文件描述符存储在 fds[1]

接着,父进程调用 fork() 创建一个子进程:

代码语言:javascript
复制
pid_t pid = fork();

此时,父进程和子进程都有一个指向同一个管道的文件描述符。

在父进程中:

  • 父进程关闭管道的读端 fds[0]
  • 父进程可以向管道的写端 fds[1] 写入数据。

在子进程中:

  • 子进程关闭管道的写端 fds[1]
  • 子进程可以从管道的读端 fds[0] 读取数据。

通过这种方式,父进程和子进程可以通过共享的管道进行通信。父进程将数据写入管道,子进程从管道中读取数据。

这个过程中,父进程和子进程各自持有管道的一端文件描述符,确保了数据的正确流向。当父进程关闭读端,子进程关闭写端时,双方就建立了一个单向的数据流通道。

使用 fork() 创建的子进程与父进程共享相同的内存空间,这意味着它们可以共享同一个管道。这种机制使得进程间通信变得简单高效,是 Unix/Linux 系统中常见的进程间通信方式之一。

🌉 站在⽂件描述符⻆度-深度理解管道

从文件描述符的角度来深入理解管道(pipe)的工作原理是非常有帮助的。让我们一步步探讨这个过程:

  1. 创建管道:
    • 使用 pipe(fds) 系统调用创建一个管道。
    • 管道包含两个文件描述符: fds[0] 是读端, fds[1] 是写端。
  1. 文件描述符的特点:
    • 文件描述符是操作系统用来标识打开的文件或其他I/O资源的整数。
    • 每个进程都有自己独立的文件描述符表,用于管理该进程打开的所有文件和资源。
    • 通常,标准输入(stdin)是文件描述符 0, 标准输出(stdout)是文件描述符 1, 标准错误(stderr)是文件描述符 2。
  2. 管道的文件描述符:
    • 当创建管道时, pipe(fds) 会在进程的文件描述符表中添加两个新的文件描述符: fds[0]fds[1]
    • fds[0] 是管道的读端, fds[1] 是管道的写端。
    • 这两个文件描述符都指向同一个内核缓冲区,即管道本身。
  3. 进程间通信:
    • 当一个进程向管道的写端 fds[1] 写入数据时,数据会被写入内核缓冲区。
    • 另一个进程可以通过读端 fds[0] 从内核缓冲区中读取数据。
    • 这样就实现了进程间的通信。
  4. 文件描述符共享:
    • 当使用 fork() 创建子进程时,子进程会继承父进程的所有文件描述符,包括管道的文件描述符。
    • 父进程和子进程共享同一个管道的文件描述符,这使得它们可以通过管道进行通信。
  5. 关闭文件描述符:
    • 当一个进程不再需要管道的某个端口时,应该关闭相应的文件描述符。
    • 关闭读端 fds[0] 表示不再从管道读取数据,关闭写端 fds[1] 表示不再向管道写入数据。
    • 当管道的读端和写端都被关闭后,管道将被内核自动关闭。

🌉站在内核⻆度-管道本质

看待管道,就如同看待⽂件⼀样!管道的使⽤和⽂件⼀致,迎合了“Linux⼀切皆⽂件思想”。

从内核的角度来理解管道(pipe)的本质是很有帮助的。让我们深入探讨一下管道在内核中的实现:

  1. 内核中的管道实现:
    • 在内核中,管道本质上是一个环形缓冲区,用于存储进程间传递的数据。
    • 这个缓冲区由内核维护,用户进程无法直接访问或修改它。
    • 管道有两个文件描述符: 读端和写端,分别用于读取和写入数据。
  2. 管道的缓冲区:
    • 管道的缓冲区大小是固定的,通常是几千字节。
    • 当写端向管道写入数据时,数据会被存储在缓冲区中。
    • 当读端从管道读取数据时,数据会从缓冲区中取出。
  3. 读写机制:
    • 写入管道时,如果缓冲区已满,写进程会被阻塞,直到有空间可写。
    • 读取管道时,如果缓冲区为空,读进程会被阻塞,直到有数据可读。
    • 这种读写阻塞机制确保了数据的完整性和正确性。
  4. 文件系统集成:
    • 在 Unix/Linux 系统中,管道也是文件系统中的一种特殊类型。
    • 管道的文件描述符可以像普通文件一样被操作,例如使用 read()write()close() 等系统调用。
    • 这使得管道可以无缝地集成到各种 I/O 操作中。
  5. 进程间通信:
    • 当两个进程共享同一个管道时,它们就可以通过这个管道进行数据交换。
    • 一个进程向管道写入数据,另一个进程从管道读取数据,实现了进程间的通信。
    • 这种通信方式简单高效,是 Unix/Linux 系统中常见的进程间通信机制之一。

总的来说,从内核的角度看,管道本质上是一个由内核维护的环形缓冲区,用于在进程间传递数据。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📝进程间通信介绍
    • 🌉进程间通信⽬的
    • 🌉进程间通信发展
    • 🌉进程间通信分类
  • 🌠管道
    • 🌉匿名管道
    • 🌉实例代码
  • 🌠三种理解管道方法
    • 🌉⽤fork来共享管道原理
    • 🌉 站在⽂件描述符⻆度-深度理解管道
    • 🌉站在内核⻆度-管道本质
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档