进程间通信(一)管道

一、概述

在Linux系统中,以进程为单位来分配和管理资源。
由于保护的缘故,一个进程不能直接访问另一个进程的资源,也就是说,进程之间互相封闭。
在一个复杂的应用系统中,通常会使用多个相关的进程来共同完成一项任务,因此要求进程之间必须能够互相通信,从而来共享资源和信息。
所以,一个操作系统内核必须提供进程间的通信机制。

管道

管道和有名管道是最早的进程间通信机制之一,管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。

管道是指用于连接一个读进程和一个写进程,以实现它们之间通信的共享方式,又称pipe文件。

管道是Linux支持的最初Unix IPC形式之一,具有以下特点:

管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;

只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);

单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

管道是所有Unix都提供的一种IPC机制

一个进程将数据写入管道,另一个进程从管道中读取数据

在shell中使用管道的例子

命令:“ls | more”

使用pipeline “|”将两个命令”ls”和“more”连接起来,使得ls的输出成为more的输入

也可以使用如下的两个命令

命令1:“ls > tmp”

命令2:”more < tmp”

命令1把ls的输出重定向到tmp文件中;

命令2把more的输入重定向到tmp文件

创建管道

管道的创建:
#include <unistd.h>
int pipe(int fd[2])
该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信。

注意:fd[0] 用于读取管道,fd[1] 用于写入管道。

管道读写

管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过fork函数创建一个子进程。

子进程写入和父进程读的命名管道:

管道的读写规则

管道两端可分别用描述字fd[0]以及fd[1]来描述。
需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字fd[0]表示,称其为管道读端;另一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。
如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。
一般文件的I/O函数都可以用于管道,如close、read、write等等。

系统文件 write(fd[1],buf,size)

功能:把buf中的长度为size字符的消息送入管道入口fd[1]
fd[1]—pipe入口
buf:存放消息的空间
size :要写入的字符长度

系统文件 read(fd[0],buf,size)

功能:从pipe出口fd[0]读出size字符的消息置入 buf中。
fd[0]――Pipe的出口

进程通信 管道 (pipe系统调用)

pipe(fd): 创建一个管道,fd[0]为管道的读端;fd[1]为管道的写端。 管道可用来实现父进程与其子孙进程之间的通信。管道以FIFO方式传送消息。

#include <stdio.h>
...... 
main() 
{ 
      int x,fd[2]; 
      char buf[30],s[30]; 
      pipe(fd); /*创建管道*/ 
      while((x=fork()) = = -1); /*创建子进程失败时,循环*/ 
      if(x = = 0) 
      { 
            sprintf(buf,“This is an example\n”); 
            write(fd[1],buf,30); /*把buf中的字符写入管道*/ 
            exit(0); 
      } 
      wait(); 
      read(fd[0],s,30); /*父进程读管道中的字符*/ 
      printf(“%s”,s); 
}

有名管道

管道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(named pipe或FIFO)提出后,该限制得到了克服。

FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间)。

因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。

有名管道的创建

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode)

该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。
第二个参数与打开普通文件的open()函数中的mode 参数相同。
如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用于FIFO,如close、read、write等等。

有名管道比管道多了一个打开操作:open。

FIFO的打开规则:

如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。

如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。

#include<sys/types.h> 
#include<sys/stat.h> 
#include<fcntl.h> 
main() 
{ 
      char buffer[80]; 
      int fd; 
      char *FIFO=“pipe1”; 
      mkfifo(FIFO,0666); 
      if(fork()>0)
      { 
             char s[ ] = "hello!\n";
             fd = open (FIFO,O_WRONLY); 
             write(fd,s,sizeof(s)); close(fd); 
      } 
      else
      { 
             fd= open(FIFO,O_RDONLY);
             read(fd,buffer,80); 
             printf("%s",buffer); close(fd); 
      } 
}

管道的局限性

从IPC的角度看,管道提供了从一个进程向另一个进程传输数据的有效方法。但是,管道有一些固有的局限性:

1.因为读数据的同时也将数据从管道移去,因此,管道不能用来对多个接收者广播数据。
2.管道中的数据被当作字节流,因此无法识别信息的边界。

如果一个管道有多个读进程,那么写进程不能发送数据到指定的读进程。同样,如果有多个写进程,那么没有办法判断是它们中哪一个发送的数据。

转载于:https://www.cnblogs.com/yanghaishu/archive/2012/05/10/2495201.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值