进程通信——匿名管道

有的时候在程序的开发过程中,两个进程之间会有数据的交互。信号的机制能够实现进程通信。它通过 “中断--响应” 的异步模式来工作。但作为通信来讲,信号还是远远不够的。因为它不能够携带任何其他的信息。只利用信号机制来实现进程通信显得捉襟见肘,并且信号的优势并不此。所以必须开发新的进程间通信的方法。本文所学习的 "匿名管道" 便是其中的一种最简单的方法。

基本概念

在讲管道的基本概念之前,首先提一个问题。如果让你来设计进程通信的方式,你会怎么做?最简单的方式莫过于两个进程打开同一个文件,这样就可以通过不断的读写操作来实现信息的交互。管道的概念与原理与上面的方法类似但不完全一样。下面来看下创建管道的函数 pipe

#include <unistd.h>
int pipe(int fd[2]);
                    返回值:若成功,返回0,若出错,返回-1

该函数返回两个文件描述符 fd[0]fd[1]。规定:fd[0]用于读操作,fd[1]用于写操作。在创建管道之后,通常的做法是调用 fork 函数,那么子进程也就继承了父进程的fd[0]fd[1]。所以,这样父进程与子进程就通过这两个文件描述符建立了一个通信的 "管道" 。在通信时,父进程与子进程要么一个关闭读端要么一个关闭写端来实现 半双工 方式的数据传递。即数据流只能往一个方向走而不能同时通信。下面的这个图有助于理解 管道 的这种工作方式:

之所以称之为 "匿名" 是因为这种技术只能适用在具有亲缘关系的两个进程中。不是任意的两个进程都可以通过这种方式来实现通信。

简单的例子

下面的这个例子利用管道来实现了,子进程向父进程传递一段信息,父进程接收到并打印出来的简单功能。

int main(void)
{
    int n;
    int fd[2];
    pid_t pid;

    char line[1024];

    if (pipe(fd) < 0)
        return;
    if ((pid = fork()) < 0)
        return;
    else if (pid > 0)
    {
        close(fd[0]);
        write(fd[1], "hello world\n", 12);
    }
    else
    {
        close(fd[1]);
        n = read(fd[0], line, 1024);
        printf("the recv msg is %s\n", line);
    }
    return(0);

}

注意点

这里记录一下,管道操作中对读端,写端操作的注意点。

  • 写端没有关闭的情况下,执行 read 操作:

    1. 如果管道中没有数据,read 将会阻塞。
    2. 管道的缓冲区也是有大小的,这个值记录在PIPE_BUF中。 read函数传入的字节数、管道中实际的字节数、管道缓冲区的大小这三者之间 会存在大小顺序的关系。read的返回值也跟着三者之间的关系有关。不过只要记住, read返回的是实际读到的字节数,并做好返回值检查就可以在编程中避免错误了。
  • 写端关闭的情形下,执行 read 操作:

    这个时候read不会出错,但是返回值为0。

  • 读端未关闭的情况下,执行 write 操作:

    如果管道的缓冲区已经满了,则write操作阻塞

  • 读端关闭的情况下,执行 write 操作:

    这种情况是危险的,write返回-1, error设为EPIPE, 并产生SIGPIPE信号。

popen pclose函数

在进程控制里面提到过,如果想在程序里面调用 shell 命令或者脚本文件,可以通过fork函数自己实现,或者直接调用 system 函数。现在我们又多了一种方法,这就是popen函数,并且popen函数通过管道机制,可以将执行shell命令的输出结果传递到父进程,或者父进程可以通过管道向shell命令传递参数。这样的实现又极大的方便了我们编程的需求。下面先写出popen pclose函数的声明,再举几个例子来加深一下印象。

#include <stdio.h>
FILE *popen(const char *cmdstring, const char *type);
                            返回值:若成功,返回文件指针;若出错,返回NULL

int pclose(FILE *fp);
                            返回值:若成功,返回cmdstring的终止状态;若出错,返回-1

popen 函数传入的 type 为 "r" 时,表示程序可以从其返回的文件指针中读取执行 cmstring 的结果,它其实将子进程的 stdout 与返回的文件指针连接在一起。如下图所示:

popen 函数传入的 type 为 "w" 时,表示父进程可以通过文件指针来向子进程传递参数,它的理解可以借助于下图:

两个简单的利用 popen 函数的例子:

/* test popen output */
int main()
{
    FILE *fp;
    pid_t pid;
    char *cmd = "ls -al";
    char line[1024];

    if ((fp = popen(cmd, "r")) == NULL)
        return;

    // get and print the data from child process
    fread(line, sizeof(char), 1024, fp);
    printf("%s\n", line);

    pclose(fp);
    return;
}

/* test popen input */
int main()
{
    FILE *fp;
    pid_t pid;
    char *cmd = "cat ";
    char *msg = "hello world\n";

    if ((fp = popen(cmd, "w")) == NULL)
        return;

    // get the params form the parent process
    fwrite(msg, sizeof(char), 12, fp);
    pclose(fp);

    return;
}
时间: 12-19

进程通信——匿名管道的相关文章

进程通信——有名管道

上一篇文章中学习了进程间通信的一种简答的方法:匿名管道.但是它只能用于具有亲缘关系的进程之间的通信.而FIFO的通信机制与之相似,却可以在任意两个进程之间通信. FIFO文件操作 创建FIFO类似于创建文件,确实,FIFO可以存在于文件系统中.下面是创建FIFO的函数: #include <sys/stat.h> int mkfifi(const char *pathname, mode_t mode); 返回值:若成功返回0,若出错返回-1 参数说明: pathname: FIFO文件的路径

进程通信-无名管道

无名管道:主要是针对进程通信的(自己感觉它有很大的局限性) 特点:它不是一个文件系统,不能按名访问,这也是它和有名管道之间最大的区别.无名管道只是一个系统内存里面的东西. 半双工模式,数据只能流向一个方向(老师给我们举得例子就是水厂的水到居民用水,不可能倒着流对吧). 进程之间通信,但是只能是有亲缘关系的进程才能进行通信?比如父子进程:因为在父子进程中,子进程拷贝父进程的数据段,这让这两个进程在通信的时候就有了相连的关系. 下面我们来看一下:无名管道的创建,写入,读取,关闭(我用的的是Liunx

Linux 进程通信之管道

管道是单向的.先进先出的,它把一个进程的输出和另一个进程的输入连接在一起.一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据.数据被一个进程读出后,将被从管道中删除,其他读进程将不能再读到这些数据.管道提供了简单的流控制机制,进程试图读空管道时,进程将阻塞.同样,管道已经满时,进程再试图向管道写入数据,进程将阻塞. 管道包括无名管道和有名管道两种,无名管道只能用于父进程和子进程间的通信,而有名管道可以用于同一系统中的任意两个进程间的通信. 无名管道由pipe()函数

进程通信 之 管道 标准流管道

2.管道(无名管道): a.只能用于具有亲缘关系的进程之间通信: b.半双工模式,具有固定的读端和写端: c.管道不是普通文件,不属于任何文件系统,只存在于内存中: d.当一个管道通过函数 int pipe(int fd[2]) 创建时,fd[0] 为固定的读端,fd[1] 为固定的写端: e.对管道的读写,其实就是对内核缓冲区的读写: 注意: I. 如果所有指向该管道读端的文件描述符都已经关闭,而写端继续写,则会收到SIGPIPE信号,通常会导致进程异常终止: II. 如果所有指向该管道写端的

进程通信和线程通信

Linux系统中的线程通信方式主要以下几种: *  锁机制:包括互斥锁.条件变量.读写锁 进程通信: 管道(PIPE):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系(父子进程)的进程间使用.另外管道传送的是无格式的字节流,并且管道缓冲区的大小是有限的(管道缓冲区存在于内存中,在管道创建时,为缓冲区分配一个页面大小). 有名管道 (FIFO): 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信. 信号(Signal): 信号是一种比较复杂的通信方式,用于通知接

进程通信、线程同步 概述

进程通信.线程同步  概述 线程同步 CRITICAL_SECTION 临界区. 同一时刻只能由一个线程访问的资源,叫临界资源,比如打印机.访问临界资源的代码叫临界代码区域. CRITICAL_SECTION对象没有句柄,不能被其他线程共享. 线程同步之 CRITICAL_SECTION . http://blog.csdn.net/chuchus/article/details/24494253 MUTEX 互斥量. MUTEX对象有句柄,所以也可用于不同进程的线程之间做同步. MUTEX有超

进程通信(一)—— 管道

父子进程可以通过管道进行数据交互,一个管道只能有一个数据流向,要实现双工通信,可以使用两个管道实现. 管道工作原理: 向内核申请管道描述符 父子进程fork()后均有该管道资源,但处于不同内存地址 通过对描述符读写实现通信 数据交互图: 单工通信代码实现: 1 #include <stdio.h> 2 #include <string.h> 3 #include <unistd.h> 4 #include <iostream> 5 6 using names

C#中使用命名管道进行进程通信的实例

原文:C#中使用命名管道进行进程通信的实例 1 新建解决方案NamedPipeExample 在解决方案下面新建两个项目:Client和Server,两者的输出类型均为"Windows 应用程序".整个程序的结构如下图所示. 2 实现项目Client Client仅包含一个名为"客户端"的窗体,如下图所示. 编写窗体后端代码,如下所示. using System; using System.IO; using System.IO.Pipes; using Syste

进程间的通信:管道

进程间的通信:管道 Linux中将命令联系到一起使用实际上就是把一个进程的输出通过管道传递给另一个进程的输入,这些都是shell封装好的,对标准输入和输出流进行了重新连接,使数据流从键盘输入经过两个程序最终输出到屏幕上.如下: cmd1|cmd2 进程管道 在两个程序之间传递数据最简单的方法就是使用popen()和pclose()了.原型如下: #include <stdio.h> FILE *popen(const char *command, const char *open_mode);