Linux进程通信之管道

        
》进程通信:是指进程之间的信息交换,所交换的内容少则一个状态或数值,多则成千上万个字节。

》之前所讲的进程之间交换信息的方法只有fork()或者exec传送打开文件,或者通过文件系统。现在我们要说明进程之间相互通信的其他技术--------IPC(InteProcess  Communication)

》IPC类型包括:半双工管道,FIFO
                        全双工管道,命名全双工管道
                        消息队列,信号量,共享存储
                        套接字,STREAMS

》管道:管道是IPC的最古老形式之一,管道有下面两种局限性。
          (1)历史上,他们都是半双工的(即数据只能在一个方向上流动),现在某些系统特供全双工管道(我们绝不可预先假定系统提供此功能)
          (2)他们只能在具有公共祖先的进程之间使用。通常情况,管道由一个进程创建,然后他在fork()一个子进程,此后父子进程即可共用一个管道(这里得说一下,之后所说的FIFO就没有这种局限性
            套接字和命名流管道没有和两种局限性。

》尽管半双工管道有这两种局限性,但他仍是最常用的IPC形式。每当你在管道中键入一个shell执行的命令序列时,shell为每一条命令单独创建一个进程,然后将前一条命令进程的标准输出用管道与后一条命令的标准输入相连接。

》管道由调用pipe函数创建

           #include <unistd.h>
           int pipe (int filedes[2]);

     $ 由输出型参数返回两个文件描述符:filedes[0]为读而打开  filedes[1]为写而打开,单个进程的管道几乎是没有作用的。通常父进程fork出子进程这样就创建了从父进程到子进程(或反向)的IPC通道。

     $数据被一个进程从管道读出后将被管道删除,其他读进程不再读到。管道提供了简单的流控制机制,未关闭对应文件描述符时,进程试图读空管道将导致进程阻塞,进程试图写已满的管道时,也将导致进程阻塞。当关闭 相应文件描述符,假设读端文件描述符都已经关闭,子进程试图写已满的管道时,将触发异常SIGPIPE信号使子进程停止并异常退出,写端文件描述符都已经关闭,子进程试图读已空的管道将读到0,相当于读到文件尾。

》管道关闭
     管道关闭只需要将两个文件描述符关掉即可,可以使用close函数关闭。

》命名管道 FIFO     
        管道的一个不足之处是没有名字,因此,只能用于具有亲缘关系的进程间通信,在命名管 道( named pipe FIFO )提出后,该限制得到了克服。 FIFO 不同于管道之处在于它提供一 路径名与之关联,以 FIFO 的文件形式存储于文件系统中。命名管道是一个设备文件,此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。值得注意的是,FIFO(first inputfirst output)总是按照先进先出的原则工作,第一个被写入的数据将首先从管道中读出。

  $命名管道的创建与读写
Linux下有两种方式创建命名管道。一是在Shell下交互地建立一个命名管道,二是在程序中使用系统函数建立命名管道。Shell方式下可使用mknodmkfifo命令,下面命令使用mknod创建了一个命名管道:
   mknod namedpipe

创建命名管道的系统函数有两个:mknodmkfifo。两个函数均定义在头文件sys/stat.h

函数原型如下:
#include <sys/types.h>
#include <sys/stat.h>
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);
函数mknod参数中path为创建的命名管道的全路径名:
                                mod为创建的命名管道的模式,指明其存取权限;
                                 dev 为设备值,该值取决于文件创建的种类,它只在创建设备文件时才会用
               这两个函数调用成功都返回0,失败都返回-1

下面使用mknod函数创建了一个命名管道:
umask(0);
if (mknod("/tmp/fifo",S_IFIFO | 0666) == -1)
{
perror("mkfifo error");
exit(1);
}
函数 mkfifo 前两个参数的含义和 mknod 相同。下面是使用 mkfifo 的示例代码:
umask(0);
if (mkfifo("/tmp/fifo",S_IFIFO|0666) == -1)
{
perror("mkfifo error!");
exit(1);
}
S_FIFO  表明创建一个命名管道并且读写权限为0666
一旦创建了FIFO就可以用open打开它,一般访问函数(open,write,read等)都可以用于命名文件。命名管道是存储于设备上的一个文件,而管道是存储于内存的一个特殊文件。 open() 打开命名管道的进程可能会被阻塞。但如果同时用读写方式 O_RDWR )打开,则一定不会导致阻塞;如果以只读方式( O_RDONLY )打开,则调 open() 函数的进程将会被阻塞直到有写方打开管道;同样以写方式( O_WRONLY )打开 也会阻塞直到有读方式打开管道。

》总结:
        1.文件系统中的路径名是全局的,各个进程都可以访问,因此可以用文件系统中的路径名来标识一个IPC通道。
        2命名管道也被称为FIFO文件,它是一种特殊类型的文件,他在文件系统中一文件名的形式存在,但他的行为却和管道类似。
       mkfifo 函数的作用是在文件系统中创建一个文件,该文件用于提供 FIFO 功能,即命名管道。 前边讲的那些管道都没有名字,因此它们被称为匿名管道,或简称管道。对文件系统来说, 匿名管道是不可见的,它的作用仅限于在父进程和子进程两个进程间进行信。而命名管 道是一个可见的文件,因此,它可以用于 任何两个进程 之间的通信,不管这两个进程是不 是父子进程,也不管这两个进程之间有没有关系。

》操作:当打开FIFO时,非阻塞标志(O_NONBLOCK)将对以后的读写产生的影响
          1.使用O_NONBLOCK:访问无法满足要求时不阻塞,立即出错返回,error是ENXIO
          2.没有使用O_NONBLOCK:访问无法满足要求时将阻塞

管道的实现:

(一)管道外部实现

         当我们定义一个管道时,这个管道是由内核管理的一个缓冲区,可以抽象为现实生活中的一个传输线路。管道的一端连接一个进程的输出,这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。 从原理上,管道利用fork机制建立,从而让两个进程可以连接到同一个PIPE上。

       在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为:

·      (1)限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不象文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。

·     (2) 读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。

(二)管道的内部实现机制


        实际上pipe并没有单独的实现数据结构,他利用了文件的在 Linux 中,借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的。有两个 file 数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。


》管道容量:
       管道是一块内存缓冲区,可用下面的程序来测试管道的容量:

<span style="font-size:18px;">  1 #include<stdio.h>                                                           
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<fcntl.h>
  6 #include<error.h>
  7 #include<string.h>
  8 #include<errno.h>
  9 
 10 
 11 int main()
 12 {
 13    int pipefd[2];
 14    if(pipe(pipefd)==-1)
 15    {
 16        printf("pipe failed\n");
 17        return -1;
 18    }
 19    int ret;
 20    int count=0;
 21    int flags=fcntl(pipefd[1],F_GETFL);
 22    fcntl(pipefd[1],F_SETFL,flags|O_NONBLOCK);
 23    while(1)
 24    {
 25        ret=write(pipefd[1],"A",1);
 26        if(ret==-1)
 27        {
 28            printf("write error\n");
 29            break;
 30        }
 31        count++;
 32    }
 33     printf("count=%d\n",count);
 34     return 0;
 35 } </span>

结果:
            

所以结果是  65536








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值