我们先来说说进程间通信(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()函数的进程将会被阻塞直到有写方打开管道;同样以写方式( O_WRONLY)打开
也会阻塞直到有读方式打开管道。

运行示例

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

那么此时我们早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”,在客户端可以收到此内容,完成简单的进程间通信。

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

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

     无名管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信: 定义函数: int pipe(int f ...

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

    整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...

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

    匿名管道pipe 函数原型: #include <unistd.h> int pipe(int fildes[2]); 参数说明 fildes是我们传入的数组,也是一个传出参数.filde ...

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

    有名管道(FIFO) 命名管道也被称为FIFO文件,是一种特殊的文件.由于linux所有的事物都可以被视为文件,所以对命名管道的使用也就变得与文件操作非常统一. (1)创建命名管道 用如下两个函数中的 ...

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

    IPC进程间通信+管道Pipe                IPC(Inter-Process Communication,进程间通信).         管道用于进程间共享数据,事实上质是共享内存 ...

  6. Linux 进程间通信 有名管道(fifo)

    有名管道特点: 1)无名管道只能用于具有亲缘关系的进程之间,这就限制了无名管道的使用范围 2)有名管道可以使互不相关的两个进程互相通信. 3)有名管道可以通过路径名来指出,并且在文件系统中可见,但内容 ...

  7. IPC 进程间通信方式——管道

    进程间通信概述 数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到. 通知时间: ...

  8. [15]APUE:pipe / FIFO

    管道 pipe 一.概述 管道(pipe / FIFO)是一种文件,属于 pipefs 文件系统类型,可以使用 read.write.close 等系统调用进行操作 其本质是内核维护了一块缓冲区与管道 ...

  9. linux中管道(pipe)一谈

    /*********************************************** 管道(pipe)是Linux上进程间通信的一种方式,其是半双工(数据流只能在一个方向上流动(还需要经过 ...

随机推荐

  1. HDU 4911 Inversion (逆序数 归并排序)

    Inversion 题目链接: http://acm.hust.edu.cn/vjudge/contest/121349#problem/A Description bobo has a sequen ...

  2. Bootstrap排版

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta na ...

  3. Android Touch事件传递机制通俗讲解

    在讲正题之前我们讲一段有关任务传递的小故事,抛砖迎玉下: 话说一家软件公司,来一个任务,分派给了开发经理去完成: 开发经理拿到,看了一下,感觉好简单,于是 开发经理:分派给了开发组长 开发组长:分派给 ...

  4. Python3.5.2官方文档学习备忘录

    网址:https://docs.python.org/3/ 虽然学习官方文档有些耗时,不过看最原版的还是感觉好一点,原汁原味没有曲解没有省略. 从命令行向Python传递参数,运行:python - ...

  5. [Android][Android Studio] *.jar 与 *.aar 的生成与*.aar导入项目方法

    主要讲解Android Studio中生成aar文件以及本地方式使用aar文件的方法. 在Android Studio中对一个自己库进行生成操作时将会同时生成*.jar与*.aar文件. 分别存储位置 ...

  6. DuiLib(一)——窗口及消息

    最近看了下开源界面库duilib的代码,写几篇相关的文章.网上已经有好多相关的文章了,我这里只是记录自己的学习过程,写到哪里算哪里,权当自娱自乐. duilib是一轻量级的direcui界面库,所谓d ...

  7. 2013年中国区Skyline软件价格体系

    < 2013年中国区Skyline软件价格体系                         序号 产品名称 描述 市场报价         1 TerraExplorer Pro 5.1 对 ...

  8. [AngularJS] ngModelController render function

    ModelValue and ViewValue: $viewValue: Actual string value in the view. $modelValue: The value in the ...

  9. mysql 5.1 到 mysql 5.2的出现的索引BTREE问题 use near 'USING BTREE

    转自:http://hi.baidu.com/our_poll/item/669c5ce885b33ff1e0a5d4fc 我本机测试是安装的 mysql 5.1 , 但服务器上确是使用的 mysql ...

  10. 关于IE8中使用Jquery load方法无法正常加载页面

    最近发现,在IE8中使用Jquery load方法时无法正常加载页面,页面显示空白,没有加载.调试发现,页面多了一个</div>标签,但在FF和CH下表现正常.希望能给遇到同样问题的码农有 ...