进程间通信之管道(pipe、fifo)

我们先来说说进程间通信(IPC)的一般目的,大概有数据传输、共享数据、通知事件、资源共享和进程控制等。但是我们知道,对于每一个进程来说这个进程看到属于它的一块内存资源,这块资源是它所独占的,所以进程之间的通信就会比较麻烦,原理就是需要让不同的进程间能够看到一份公共的资源。所以交换数据必须通过内核,在内核中开辟?块缓冲区,进程1把数据从?户空间 拷到内核缓冲区,进程2再从内核缓冲区把数据读?,内核提供的这种机制称为进程间通信。一般我们采用的进程间通信方式有

  1. 管道(pipe)和有名管道(FIFO)
  2. 信号(signal)
  3. 消息队列
  4. 共享内存
  5. 信号量
  6. 套接字(socket)

我们先来从最简单的通信方式来说起;

匿名管道 pipe

---------------------------------------------------------------------------------------------------------------------------------

管道的创建

管道是一种最基本的进程间通信机制。管道由pipe函数来创建:

调用pipe函数,会在内核中开辟出一块缓冲区用来进行进程间通信,这块缓冲区称为管道,它有一个读端和一个写端。

pipe函数接受一个参数,是包含两个整数的数组,如果调用成功,会通过pipefd[2]传出给用户程序两个文件描述符,需要注意pipefd [0]指向管道的读端, pipefd [1]指向管道的写端,那么此时这个管道对于用户程序就是一个文件,可以通过read(pipefd [0]);或者write(pipefd [1])进行操作。pipe函数调用成功返回0,否则返回-1..

那么再来看看通过管道进行通信的步骤:

》父进程创建管道,得到两个文件描述符指向管道的两端

》利用fork函数创建出子进程,则子进程也得到两个文件描述符指向同一管道

》父进程关闭读端(pipe[0]),子进程关闭写端pipe[1],则此时父进程可以往管道中进行写操作,子进程可以从管道中读,从而实现了通过管道的进程间通信。

示例代码:

#include<stdio.h>
#include<unistd.h>
 #include<string.h>
int main()
{
int _pipe[2];
int ret=pipe(_pipe);
    if(ret<0)
    {
         perror("pipe\n");
    }
  pid_t id=fork();
  if(id<0)
{
       perror("fork\n");
   }
   else if(id==0)  // child
    {
        close(_pipe[0]);
        int i=0;
        char *mesg=NULL;
       while(i<100)
       {
           mesg="I am child";
           write(_pipe[1],mesg,strlen(mesg)+1);
           sleep(1);
           ++i;
        }
     }
    else  //father
   {
       close(_pipe[1]);
         int j=0;
        char _mesg[100];
         while(j<100)
        {
          memset(_mesg,‘\0‘,sizeof(_mesg ));
          read(_pipe[0],_mesg,sizeof(_mesg));
          printf("%s\n",_mesg);
          j++;
        }
    }
   return 0;
}

结果演示:

pipe的特点:

1. 只能单向通信

2. 只能血缘关系的进程进行通信

3. 依赖于文件系统

4、生命周期随进程

5. 面向字节流的服务

6. 管道内部提供了同步机制

说明:因为管道通信是单向的,在上面的例子中我们是通过子进程写父进程来读,如果想要同时父进程写而子进程来读,就需要再打开另外的管道;

管道的读写端通过打开的?件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那?继承管道?件描述符。 上?的例?是?进程把?件描述符传给?进程之后??进程之 间通信,也可以?进程fork两次,把?件描述符传给两个?进程,然后两个?进程之间通信, 总之 需要通过fork传递?件描述符使两个进程都能访问同?管道,它们才能通信。

四个特殊情况:

》 如果所有指向管道写端的?件描述符都关闭了,?仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到?件末尾?样

》 如果有指向管道写端的?件描述符没关闭,?持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

》 如果所有指向管道读端的?件描述符都关闭了,这时有进程指向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终?。

》 如果有指向管道读端的?件描述符没关闭,?持有管道写端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再write会阻塞,直到管道中有空位置了才写?数据并返回。

命名管道FIFO

---------------------------------------------------------------------------------------------------------------------------------

在管道中,只有具有血缘关系的进程才能进行通信,对于后来的命名管道,就解决了这个问题。FIFO不同于管道之处在于它提供?个路径名与之关联,以FIFO的?件形式存储于?件系统中。命名管道是?个设备?件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。值得注意的是, FIFO(first input first output)总是按照先进先出的原则?作,第?个被写?的数据将?先从管道中读出。

命名管道的创建

创建命名管道的系统函数有两个: 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。下?使?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_IFIFO|0666"指明创建?个命名管道且存取权限为0666,即创建者、与创建者同组的
?户、其他?户对该命名管道的访问权限都是可读可写( 这?要注意umask对?成的
管道?件权限的影响) 。
命名管道创建后就可以使?了,命名管道和管道的使??法基本是相同的。只是使?命
名管道时,必须先调?open()将其打开。因为命名管道是?个存在于硬盘上的?件,?管道
是存在于内存中的特殊?件。
需要注意的是,调?open()打开命名管道的进程可能会被阻塞。但如果同时?读写?式
( O_RDWR)打开,则?定不会导致阻塞;如果以只读?式( O_RDONLY)打开,则调
?open()

运行示例

-----------------------------------------------------------

那么此时我们早server.c中创建命名管道并打开,对管道中进行写操作,在client.c中进行读操作,把读到的内容进行打印,就实现了我们的使用命名管道通信。

Server.c:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#define _PATH_NAME_ "/tmp/file.tmp"
#define _SIZE_ 100

 int main()
 {
      int ret=mkfifo(_PATH_NAME_,S_IFIFO|0666);
      if(ret==-1){
           printf("make fifo error\n");
           return 1;
      }
     char buf[_SIZE_];
     memset(buf,‘\0‘,sizeof(buf));
     int fd=open(_PATH_NAME_,O_WRONLY);
     while(1)
     {
         //scanf("%s",buf);
         fgets(buf,sizeof(buf)-1,stdin);
         int ret=write(fd,buf,strlen(buf)+1);
         if(ret<0){
         printf("write error");
         break;
         }
     }
      close(fd);
      return 0;
 }                    

Client.c:
#include<stdio.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#define _PATH_NAME "/tmp/file.tmp"
#define _SIZE_ 100
int main()
{
    int fd=open(_PATH_NAME,O_RDONLY);
    if(fd<0){
        printf("open file error");
        return 1;
    }
   char buf[_SIZE_];
   memset(buf,‘\0‘,sizeof(buf));
   while(1)
   {
       int ret=read(fd,buf,sizeof(buf));
       if(ret<0){
           printf("read end or error\n");
           break;
       }
   printf("%s",buf);
   }
   close(fd);
   return 0;
 }  

结果演示:

   

可以看到我实在服务端发送了“Hello” “I am aerver”,在客户端可以收到此内容,完成简单的进程间通信。

时间: 2024-10-28 21:54:37

进程间通信之管道(pipe、fifo)的相关文章

Linux 进程间通信之管道(pipe),(fifo)

 无名管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信: 定义函数: int pipe(int filedes[2]) filedes[0]为管道里的读取端 filedes[1]则为管道的写入端. 实现机制: 管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条.管道的一端连接一个进程的输出.这个进程会向管道中放入信息.管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息.一个缓

Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信: 实现机制: 管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条.管道的一端连接一个进程的输出.这个进程会向管道中放入信息.管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息.一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管

进程间通信之管道--pipe和fifo使用

匿名管道pipe 函数原型: #include <unistd.h> int pipe(int fildes[2]); 参数说明 fildes是我们传入的数组,也是一个传出参数.fildes[0]是读端,fildes[1]是写端. 返回值 成功调用返回0. 失败调用返回-1且设置errno. 实例 现在实现一个用父进程读,子进程写的管道例子. int main(int argc, char const *argv[]) { int pipefd[2]; pipe(pipefd); pid_t

【IPC第二个进程间通信】管道Pipe

IPC进程间通信+管道Pipe         IPC(Inter-Process Communication,进程间通信).         管道用于进程间共享数据,事实上质是共享内存.经常使用IPC之中的一个.管道不仅能够用于本机进程间通信,还可实现跨网络进程间通信,如同Socket通信,管道相同封装计算机底层网络实现,提供一个良好的API接口.                1.管道(Pipe):        管道分为匿名管道和命名管道.        匿名管道仅仅能用于父子进程间通信

linux 进程间通信-有名管道(FIFO)

有名管道(FIFO) 命名管道也被称为FIFO文件,是一种特殊的文件.由于linux所有的事物都可以被视为文件,所以对命名管道的使用也就变得与文件操作非常统一. (1)创建命名管道 用如下两个函数中的其中一个,可以创建命名管道. #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *filename, mode_t mode); int mknod(const char *filename, mode

linux进程间通信-有名管道(FIFO)

有名管道(FIFO) 命名管道也被称为FIFO文件,是一种特殊的文件.由于linux所有的事物都可以被视为文件,所以对命名管道的使用也就变得与文件操作非常统一. (1)创建命名管道 用如下两个函数中的其中一个,可以创建命名管道. #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *filename, mode_t mode); int mknod(const char *filename, mode

进程间通信(4) - 管道(pipe)

1. 前言 本篇文章的所有例子,基于RHEL6.5平台.本篇只介绍管道(匿名管道/普通管道),命名管道在后续文章中会介绍. 2.管道特性 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: **管道是半双工的,数据只能向一个方向流动,一端输入,另一端输出.需要双方通信时,需要建立起两个管道. **管道分为普通管道和命名管道.普通管道位于内存,只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程).命名管道位于文件系统,没有亲缘关系的进程间只要知道管道名也可以通讯. **管道也是文

简述Linux进程间通信之管道pipe(下)

上篇文章的简述,我相信大家对管道的概念有了模糊的认识,本文通过代码实例来强化对管道的理解. 创建管道主要用到pipe函数,pipe的原型如下: 一.函数原型 #include <unistd.h> int pipe(int pipefd[2]); 参数:一个整型数组,管道创建成功后,pipefd[0]表示管道的读端,pipefd[1]表示管道的写端. 成功返回0,失败返回-1,同时errno被设置. 二.父子进程通信 上文中,我们描述了父子进程通过管道来进行通信的整个过程,现在我们用C语言来实

进程间通信IPC—匿名管道(pipe)和命名管道(fifo)

管道内部如何实现-大小,组织方式,环形队列? 一.进程间通信有多种方式,本文主要讲解对管道的理解.管道分为匿名管道和命名管道. (1)管道( pipe ):又称匿名管道.是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲缘关系通常是指父子进程关系. (2)命名管道 (named pipe或FIFO) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信. 二.管道 1. 管道的特点: (1)管道是半双工的,数据只能向一个方向流动:双方通信时,需要