Linux匿名管道及命名管道

3/3/2017来源:C/C++教程人气:2009

今天我们来说一下管道:

管道是一种最基本的ipC机制,由pipe函数创建。

管道分为匿名管道和命名管道。

匿名管道的工作流程:

1.父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。

2.父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。

3.父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。

匿名管道的几种情景:

1.写端关闭,读端一直读。那么当管道中所有数据都被读取后,再次read时会返回0,就像读到文件末尾一样。

while(1){

     ssize_tret = read(pipefd[0],buf,sizeof(buf)-1);

     buf[ret]= '\0';

         if(ret>0){

             PRintf("%s\n",buf);

        }elseif(ret == 0){

             printf("pipeis close\n");

             exit(2);

      }

结果:

           

会返回pipe is close。

 

2.写端不写,读端一直读。那么当管道中剩余的数据被读完后,再次读时会阻塞,直到管道中有数据可读才读数据。

           思路:先让写端写数据,然后让写端sleep 5秒,看读端是否会退出或者等待读。

           结果:读端一直阻塞式等待读取写端数据。

 

3.写端一只写,读端不读。阻塞。

我们通过此种方法可以验证管道的大小。

fcntl()可以改变已打开的文件性质

F_GETFL   取得fd的文件状态标志,如同下面的描述一样(arg被忽略),在说明open函数时,已说明了文件状态标志。不幸的是,三个存取方式标志 (O_RDONLY , O_WRONLY , 以及O_RDWR)并不各占1位。(这三种标志的值各是0 , 1和2,由于历史原因,这三种值互斥 — 一个文件只能有这三种值之一。) 因此首先必须用屏蔽字O_ACCMODE相与取得存取方式位,然后将结果与这三种值相比较。      

F_SETFL   设置给arg描述符状态标志,可以更改的几个标志是:O_APPEND,O_NONBLOCK,O_SYNC 和 O_ASYNC。而fcntl的文件状态标志总共有7个:O_RDONLY , O_WRONLY , O_RDWR , O_APPEND , O_NONBLOCK , O_SYNC和O_ASYNC

可更改的几个标志如下面的描述:

O_NONBLOCK  非阻塞I/O,如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,则read或write调用将返回-1和EAGAIN错误

O_APPEND    强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志

 O_DIRECT     最小化或去掉reading和writing的缓存影响。系统将企图避免缓存你的读或写的数据。如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响。如果这个标志用的不够好,将大大的降低性能

O_ASYNC      当I/O可用的时候,允许SIGIO信号发送到进程组。

 

int  main()

 {

      intpipefd[2] = {0};

     if(pipe(pipefd) < 0)

      {

         perror("pipe");

         return 1;

      }

         int count = 0;

         int ret;

         int flag=fcntl(pipefd[1],F_GETFL);

         fcntl(pipefd[1],F_SETFL,flag|O_NONBLOCK);

         while(1){

             ret = write(pipefd[1],"a",1);

             if(ret == -1)

             {

                  perror("write\n");

                  break;

             }

             count++;

         }

printf("count= %d\n",count);

        return0;

 }

结果:

所以为64K。

4.读端关闭,写端一只写。那么该进程会受到信号SIGPIPE,会导致进程异常终止。

           思路:读端读取5条数据就关闭读端(close(pipefd[0]))。然后让父进程等待子进程退出。(waitpid)并查看他的signal,发现signal为13,我们查询kill –l指令时,13号为sigpipe。

           代码:

while(count--){

ssize_tret = read(pipefd[0],buf,sizeof(buf)-1);                                                      

   buf[ret] = '\0';

   if(ret  > 0){

      printf("%s\n",buf);

   }else if(ret == 0){

       printf("pipe isclose\n");

       exit(2);

   }

   if(count == 0)

   {

       close(pipefd[0]);

       break;

   }

}

int status = 0;

pid_t _s = waitpid(id,&status,0);

if(_s>0){

    printf("id code: %d signal:%d\n",(status>>8)&0xff,status&0xff);

}

结果:

 

说完匿名管道,我们来说一下命名管道:

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

命名管道的创建与读写

     linux下有两种方式创建命名管道。一是在Shell下交互地建立一个命名管道,二是在程序中使用系统函数建立命名管道。Shell方式下可使用mknod或mkfifo命令,下面命令使用mknod创建了一个命名管道:

创建命名管道的系统函数有两个:mknod和mkfifo。两个函数均定义在头文件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。

函数mkfifo前两个参数的含义和mknod相同。下面是使用mkfifo的示例代码:

umask(0);

if(mkfifo("/tmp/fifo",S_IFIFO|0666) == -1)

{

perror("mkfifoerror!");

exit(1);

}

“S_IFIFO|0666”指明创建一个命名管道且存取权限为0666,即创建者、与创建者同组的用户、其他用户对该命名管道的访问权限都是可读可写(这里要注意umask对生成的管道文件权限的影响)。

命名管道创建后就可以使用了,命名管道和管道的使用方法基本是相同的。只是使用命名管道时,必须先调用open()将其打开。因为命名管道是个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。

需要注意的是,调用open()打开命名管道的进程可能会被阻塞。但如果同时用读写方式(O_RDWR)打开,则一定不会导致阻塞;如果以只读方式(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写方打开管道;同样以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。

示例:

 

 

总结:

管道的特点:

1.       只能进行单向数据通信。

2.       匿名管道只能用于进行有血缘关系关系(常用于父子进程)进程间通信,命名管道用于非血缘关系进程间通信。

3.       管道是面向字节流的,我们写入的东西都会被当作字节流。

4.       管道的生命周期是随进程的,进程退出管道也退出。

5.       只有当管道中有数据,读端才读,自主完成同步操作。带有自主保护机制。