<h4>进程间通信 fork pipe pie_t 等用法(管道机制 通信)</h4>
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲 区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。如下图所示。

<a href="http://www.emacsvi.com/wp-content/uploads/2015/10/IPC_pipe_ie.jpg"><img src="http://www.emacsvi.com/wp-content/uploads/2015/10/IPC_pipe_ie.jpg" alt="IPC_pipe_ie" width="288" height="176" class="alignnone size-full wp-image-291" /></a>

<h4> 管道 pipe()函数 </h4>
管道是一种最基本的IPC机制,由pipe函数创建:

<pre lang="c" escaped="true">
#include <unistd.h>

int pipe(int pipefd[2]);
</pre>

调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述 符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来 就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。 pipe函数调用成功返回0,调用失败返回-1。

<blockquote>int pipe(int filedes[2]) 中的两个文件描述符被强制规定filedes[0]只能指向管道的读端,如果进行写操作就会出现错误;同理filedes[1]只能指向管道的写端,如果进行读操作就会出现错误.</blockquote>

开辟了管道之后如何实现两个进程间的通信呢?比如可以按下面的步骤通信。

<a href="http://www.emacsvi.com/wp-content/uploads/2015/10/IPC_pipe.jpg"><img src="http://www.emacsvi.com/wp-content/uploads/2015/10/IPC_pipe-254x300.jpg" alt="IPC_pipe" width="254" height="300" class="alignnone size-medium wp-image-292" /></a>

<ol>

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

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

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

</ol>

注:之前一直没明白为什么在这里父进程要关闭管道读端,并且子进程要关闭管道写端,想了很久终于想通了...,原因如下:因为上面的这个程序是要模拟父进程和子进程的管道读写操作,其中父进程用于向管道中写入数据,子进程用于向管道中读取数据,因此开始要关闭父进程的读文件描述符filedes[0], 以及关闭子进程的写文件描述符filedes[1],这是为了模拟这个过程。

然后至于为什么父进程关闭管道的读文件描述符filedes[0]后子进程还能读取管道的数据,是因为系统维护的是一个文件的文件描述符表的计数,父子进程都各自有指向相同文件的文件描述符,当关闭一个文件描述符时,相应计数减一,当这个计数减到0时,文件就被关闭,因此虽然父进程关闭了其文件描述符filedes[0],但是这个文件的文件描述符计数还没等于0,所以子进程还可以读取。也可以这么理解,父进程和子进程都有各自的文件描述符,因此虽然父进程中关闭了filedes[0],但是对子进程中的filedes[0]没有影响

文件表中的每一项都会维护一个引用计数,标识该表项被多少个文件描述符(fd)引用,在引用计数为0的时候,表项才会被删除。所以调用close(fd)关闭子进程的文件描述符,只会减少引用计数,但是不会使文件表项被清除,所以父进程依旧可以访问

最后需要注意,在linux的pipe管道下,在写端进行写数据时,不需要关闭读端的缓冲文件(即不需要读端的文件描述符计数为0),但是在读端进行读数据时必须先关闭写端的缓冲文件(即写端的文件描述符计数为0)然后才能读取数据。

<h4> manpage给的一个管道例子:</h4>
<pre lang="c" escaped="true">
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;

if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}

if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}

cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}

if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */

while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);

write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);

} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}

}
</pre>

<h4>使用管道有一些限制:</h4>
<ol>

<li> 两个进程通过一个管道只能实现单向通信,比如上面的例子,父进程写子进程读,如果有时候也需要子进程写父进程读,就必须另开一个管道。请读者思考,如果只开一个管道,但是父进程不关闭读端,子进程也不关闭写端,双方都有读端和写端,为什么不能实现双向通信?</li>

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

使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):
1. 如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
2. 如果有指向管道写端的文件描述符没关闭(管道写端的引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
3. 如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。在第 33 章 信号会讲到怎样使SIGPIPE信号不终止进程。
4. 如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。
管道的这四种特殊情况具有普遍意义。在第 37 章 socket编程要讲的TCP socket也具有管道的这些特性。

pipe()管道最基本的IPC机制的更多相关文章

  1. Anciroid的IPC机制-Binder概述

    在Linux系统中,是以进程为单位分配和管理资源的.出于保护机制,一个进程不能直接访问另一个进程的资源,也就是说,进程之间互相封闭.但是,在一个复杂的应用系统中,通常会使用多个相关的进程来共同完成一项 ...

  2. IPC机制--Binder

    文章来自 Android技术内幕 系统卷 转:http://www.linuxidc.com/Linux/2011-08/40508.htm 什么是IPC机制以及IPC机制的种类 在Linux中,是以 ...

  3. IPC机制

    转:http://blog.chinaunix.net/uid-26125381-id-3206237.html  IPC 三种通信机制 2012-05-13 17:23:55 最近看了,IPC三种通 ...

  4. 深入理解Android IPC机制之Binder机制

    Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...

  5. 20155202 张旭 课下作业: Linux下IPC机制

    20155202张旭 Linux下IPC机制 IPC机制定义 在linux下的多个进程间的通信机制叫做IPC(Inter-Process Communication),它是多个进程之间相互沟通的一种方 ...

  6. 2017-2018-1 20155222 《信息安全系统设计基础》第10周 Linux下的IPC机制

    2017-2018-1 20155222 <信息安全系统设计基础>第10周 Linux下的IPC机制 IPC机制 在linux下的多个进程间的通信机制叫做IPC(Inter-Process ...

  7. 20155239吕宇轩 Linux下IPC机制

    20155239吕宇轩 Linux下IPC机制 - 共享内存 原理:把所有需要使用的共享数据都存放在共享内存 区域中,任何想要访问这些共享数据的进程都必须在自己的进程地址空间中新增加一块内存区域,用来 ...

  8. Linux下的IPC机制

    Linux下的IPC机制 IPC(Inter-Process Communication)是多个进程之间相互沟通的一种方法.在linux下有多种进程间通信的方法. 共享内存 Linux内存共享有多种, ...

  9. Linux下IPC机制

    Linux下IPC机制 实践要求 研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接 共享内存 管道 FIFO 信号 消息队列 IPC 进程间通信(IPC,Inter ...

随机推荐

  1. 使用时间戳引入css、js文件

    前言 最近在一家创业公司实习,主要负责新版官网和商家平台管理系统的前端开发和维护,每次测试都要上传文件到ftp服务器端测试,初期由于更新修改比较频繁,每次都是直接上传覆盖css.js.php文件,链接 ...

  2. 安装mysql之后,存入中文出现乱码 02

    现在出现这个问题,将编码字符串改成utf8 之后 数据表 还是不能存储中文. 看如下两张图,应该会有启发: 这下应该明白了吧.

  3. JAVA WEB新进展

    哈哈,终于搞来页面来了,丑是丑,但是好的进展. 高手的思路,自己用库建连接池,确实利害. 最关键的一个HELPER代码: package org.smart4j.chapter2.helper; im ...

  4. spring_150805_datasource

    实体类: package com.spring.model; public class DogPet { private int id; private String name; private in ...

  5. Struts2.0 去掉action后缀名

    刚刚接触Struts2.0,发现默认请求都会带着后缀名:action 就如下图,url地址中会暴露login.action(请原谅struts拼写错误..) 作为一个URL简洁爱(chu)好(nv)者 ...

  6. SQL语句 递归

    --正向递归查询(根据ID查到自己和自己以下的所有数据)   connect by prior a.id = a. parentid   --反向递归查询(根据叶子ID查出自己和自己以上的根数据)   ...

  7. Java:正则表达式的详解

    正则表达式:符合一定规则的表达式. 作用:用于专门操作字符串. 特点:用一些特定的符号来表示一些代码的操作.这样就简化书写.所以学习正则表达式就是学习一些特殊符号的使用. 好处:可以简化对字符串的操作 ...

  8. 自己动手实现java中cache

    实现思路: 创建一个静态Hashtable用于保存key和value,对于cache过期后的方法回调,在cache过期后,再访问cache的时候进行,避免了使用定时器轮询过期时间,进行cache清除的 ...

  9. jmeter之配置文件介绍

    jmeter.bat, jmeter.properties在installpath/bin目录下 jmeter.bat文件包含如下内容: set HEAP=-Xms512m(初始化堆内存大小) -Xm ...

  10. 蒙特罗卡π算法(C++语言描述)

    圆的面积计算公式为:S=π*r*r 将圆放到一个直角坐标系中,如图黄色部分的面积是S/4=(π*r*r)/4;如果我们将取一个单位圆,则S/4=π/4. 因为是单位圆,半径为1,所以图中红色正方形的面 ...