进程间通信——管道(匿名与命名)

物理上分,可以将管道分为同主机的进程之间的通信不同主机间的进程之间的通信。从通信方式上来分,管道又分为匿名管道命名管道,下面就匿名管道和命名管道的特性作以阐述。

匿名管道(pipe)

[含义]:管道是一个进程的数据流到另一个进程的通道,即一个进程的数据的输出作为另一个进程的数据的输入,管道起到桥梁作用

比如:当我们输入:ls -l | cat test 其中的 ls 和 cat 是两个进程, | 代表管道,意思是执行 ls -l 进程,并将输出结果作为cat test 进程的输入,cat 进程将输入的结果打印在屏幕上。

[本质]:匿名管道之所以可以通信的本质在于,父进程frok子进程,父子进程各自拥有一个文件描述符表,但是两者的内容是一样的,既然内容一样,那么指向的就是同一个管道,即父子进程看到了同一份公共资源。

[管道的创建]:管道是一种最基本的IPC机制,由pipe函数创建:

int pipe(fd[2]);

fd :文件描述符数组

fd[2]:表示管道的输入输出端,输出端数据经过管道流到输入端,函数执行完后,会将这个数组赋值。

fd[0]:表示管道输入端的文件描述符。

fd[1]:表示管道输出端的文件描述符。

[进程间通信示意图]:我们让父进程关闭管道读端,子进程关闭管道写端。父进程可以给管道里面写,子进程可以从管道里面读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。


#include<stdio.h>
#include<unistd.h>
#include<string.h>

int main()
{
	int fd[2];
	int ret = pipe(fd);
	if(ret == -1)
	{
		printf("create pipe error!\n");
		return 1;
	}
	pid_t id = fork();
        if(id < 0)
	{
		printf("frok child error!\n");
		return 2;
	}
	else if(id == 0)
	{
		//child
		close(fd[1]); //close write
		char msg[1024];
		int i = 0;
		while(i < 1024)
		{
			memset(msg,'\0',sizeof(msg));
			read(fd[0],msg,sizeof(msg));
			printf("%s\n",msg);
			++i;
		}
	}
	else
	{
		//father
		close(fd[0]); //close read
		int j = 0;
		char* str = NULL;
		while(j < 1024)
		{
			str = "i am child";
			write(fd[1],str,strlen(str)+1);
			sleep(1);
			j++;
		}
	}
	return 0;
}

使用管道是有一些限制的,两个进程通过一个管道只能实现单向通信。比如上述例子,父进程写,子进程读,如果需要子进程写,父进程读,就必须另开一个管道。

[管道的五大特性]

(1)匿名管道只能单向通信。

(2)管道只能进行在有血缘关系的进程间通信,通常用于父子进程。

(3)管道通信依赖于文件系统,即管道的生命周期随进程。

(4)管道的通信被称为面向字节流,与通信格式没有关系。

(5)管道自带同步机制,保证读写顺序一致。

[思考题]:如果只开一个管道,但是父进程不关闭读端,子进程不关闭写端,双方都保留读写端,为什么不能实现双向通信?

解析:管道的读写端是通过打开的文件描述符来传递的,因此要通信的两个进程必须从他们的公共祖先那里继承管道的文件描述符。

[匿名管道的四种特殊情况]:假设都是阻塞I/O操作,没有设置O_NONBLOCK标志。

(1)如果所有指定管道写端的文件描述符都关闭了(管道写端的引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据被读取后,再次read会返回0,就像读到文件末尾一样。

(2)如果有指向管道写端的文件描述符没有关闭(管道写端的引用计数大于0),而持有写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

(3)如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进程向管道写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。

(4)如果有指向管道读端的文件描述符没有关闭(管道读端的引用计数大于0),而持有管道读端的进程也没有从管道中读取数据,这时有进程向管道的写端写入数据,那么管道被写满时,再次 write 会阻塞,直到管道中有空位置了才写入数据并返回。

命名管道(FIFO)

[本质]:命名管道在某种程度上可以看做是匿名管道,但它打破了匿名管道只能在有血缘关系的进程间的通信。命名管道之所以可以实现进程间通信在于通过同一个路径名而看到同一份资源,这份资源以FIFO的文件形式存于文件系统中。

值得注意的是,FIFO总是按照先入先出的原则工作,第一个被写入的数据将首先从管道读出。

[管道的创建]:我们可以使用下列函数之一来创建命名管道。

int mkfifo(const char* filename,mode_t mode);
int mknod(const char* filename,mode_t mode,dev_t dev);

这两个函数都可以创建一个FIFO文件,注意是创建一个真实存在于文件系统的文件。

[filename]:指定了文件名。

[mode]:指定了文件的读写权限。

mknod是比较老的函数,而mkfifo函数更加简单和规范,所以建议在可能情况下,尽量使用mkfifo。

[作用]:在文件系统中创建一个文件,该文件用于提供FIFO功能,即命名管道。对文件系统来说,匿名管道是不可见的,它的作用仅限于在父进程与子进程之间的通信。而命名管道是一个可见的文件,因此,它可以用于任何两个进程之间的通信。


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Dart 是 Google 开发的编程语言,可以用来开发移动应用和 web 应用。命名管道是 UNIX 系统中的一种 IPC(进程间通信)方式,可以在不同进程之间传递数据。在 Dart 中,可以使用 dart:io 库中的 NamedPipe 类来操作命名管道。 下面是一个简单的 Unix 示例,演示了如何在两个不同的 Dart 程序之间使用命名管道进行通信: 程序1(sender.dart) ```dart import 'dart:io'; main() async { var pipe = await NamedPipeServer.create('/tmp/my_pipe'); var sink = pipe.sink; sink.write('Hello from sender!'); await sink.flush(); await sink.close(); } ``` 程序2(receiver.dart) ```dart import 'dart:io'; main() async { var pipe = await NamedPipeClient.connect('/tmp/my_pipe'); var input = pipe.input; var message = await input.read(); print(message); await input.close(); } ``` 在这个示例中,程序 1 使用 NamedPipeServer.create() 创建了一个名为 /tmp/my_pipe 的命名管道,并向其中写入了一条消息 "Hello from sender!"。程序 2 使用 NamedPipeClient.connect() 连接到了同一个命名管道,并从中读取了消息,最后打印出来。 ### 回答2: Dart是一种跨平台的编程语言,支持在不同的操作系统上进行开发。在Unix系统中,进程间通信一个非常重要的概念。命名管道是一种用于在不同进程之间进行通信的机制。 在Dart中,可以使用dart:io包来实现使用命名管道进行进程间通信的功能。首先,需要创建一个命名管道。可以使用File类的static方法mkfifo来创建一个命名管道文件。 具体示例代码如下: import 'dart:io'; void main() { String pipePath = './mypipe'; // 命名管道的文件路径 Process.start('mkfifo', [pipePath]).then((Process process) { print('命名管道已创建'); // 接下来可以进行进程间通信的操作 }); } 上面的示例代码中,首先使用Process.start方法调用mkfifo命令来创建一个命名管道文件,然后通过then回调函数来监听创建命名管道的结果,并在命名管道创建成功后进行进一步的处理。 在创建命名管道后,就可以使用命名管道进行进程间通信了。可以使用File类来打开命名管道,并进行读写操作。具体的进程间通信的操作可以根据需求进行自定义。 需要注意的是,使用命名管道进行进程间通信时,需要确保读、写操作的顺序正确,以避免出现阻塞的情况。此外,命名管道也可以用于进程间的同步操作,例如等待另一个进程完成某个指定的任务后再进行下一步操作。 总而言之,Dart可以通过使用dart:io包来实现在Unix系统中使用命名管道进行进程间通信的功能。通过创建命名管道文件,然后使用File类打开该文件进行读写操作,可以实现不同进程之间通信与同步。具体的操作可以根据需求进行自定义。 ### 回答3: 在Unix系统中,进程间通信是非常重要的,为了实现进程间的数据传递,可以使用命名管道。Dart语言也提供了类似的方法来实现进程间通信命名管道是一种特殊的文件,它可以用来实现进程间的读写操作。在Unix示例中,首先我们需要创建一个命名管道,在Dart中可以使用File类的create方法来创建文件。创建命名管道的操作类似于创建普通文件,只是我们需要给文件一个特殊的名称,通常以“/tmp/”开头。 在创建完命名管道之后,我们可以使用File类的open方法来打开管道。在Unix示例中,我们需要使用O_RDONLY或者O_WRONLY标识符来指定管道的读写模式。然后,我们可以使用File类的read方法来读取管道中的数据,使用write方法来写入数据。 在进程间通信时,通常会有一个进程负责写入数据,另一个进程负责读取数据。这两个进程可以通过访问同一个命名管道实现通信。读取进程会在打开管道之后一直等待数据的到来,直到写入进程管道中写入数据。然后读取进程会读取数据并进行相应的处理。 需要注意的是,在使用命名管道时,需要保证写入和读取的进程顺序是正确的,否则可能会产生读写阻塞的问题。此外,写入和读取的进程使用管道时需要进行同步,以免出现数据竞争的情况。 总结起来,Dart语言可以使用命名管道实现进程间通信,类似于Unix中的示例。使用命名管道可以在进程间传递数据,实现数据的读写操作。但是需要注意保证进程的顺序和同步,以确保通信的正确性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值