Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2039220
  • 博文数量: 610
  • 博客积分: 11499
  • 博客等级: 上将
  • 技术积分: 5511
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-12 19:27
文章分类

全部博文(610)

文章存档

2016年(5)

2015年(18)

2014年(12)

2013年(16)

2012年(297)

2011年(45)

2010年(37)

2009年(79)

2008年(101)

分类: LINUX

2009-10-24 00:37:16

第13章 进程间通信:管道

在第11章,我们了解了使用信号在两个进程之间发送消息的一个简单方法。我们创建了可以用来引起响应的通知事件,但是所传递的信息限制于一个信号数量。

在这一章,我们将会了解管道,这会允许在进程之间交换更为有用的数据。在本章的结尾,我们将会使用我们的新知识来重新将CD数据库程序实现为一个非常简单的客户/服务器程序。

在本章,我们将会涉及下列主题:

管道的定义
处理管道
管道调用
父子进程
有名管道:FIFO
客户端/服务器方法

什么是管道?

当我们由一个进程到另一个进程连接数据流时我们使用术语管道。通常我们将一个进程的输出连接到另一个进程的输入。

大多数Linu用户已经很熟悉将shell命令连接到一起的思想,从而一个进程的输入可以直接连接到另一个进程的输入。对于shell命令,这是用下面的方式来输入的:

cmd1 | cmd2

shell安排标准输入与两个命令的输出,从而
cmd1的标准输入来自由终端键盘
cmd1的标准输出作为标准输入连接cmd2
cmd2的标准输出连接到终端屏幕

实际上,shell所完成的工作就是重新连接标准输入与输出流,从而由键盘输入的数据流可以通过两个命令,然后输出到屏幕。下图是这个过程的一个可视化表示。

在这一章,我们将会了解如何在一个程序中实现这种效果,以及我们如何使用管道来连接多个进程从而使得我们实现一个简单的客户端/服务器系统。

处理管道

也许在两个程序之间传递数据最简单的方法就是使用popen与pclose函数了。这两个函数原型如下:

#include
FILE *popen(const char *command, const char *open_mode);
int pclose(FILE *stream_to_close);

popen

popen函数允许一个程序调用另一个程序作为一个新的进程,并且或者向其发送数据或者由其接收数据。command字符串是要运行的程序名字及其参数。open_mode必须或者是"r"或者是"w"。

如果open_mode是"r",由被调用程序的输出可以为调用程序所用,并且可以使用通常的stdio库函数中用于读取的函数由popen函数所返回的文件流 FILE *中进行读取。然而,如果open_mode是"w",程序可以使用fwrite调用向被调用的程序发送数据。被调用的程序可以由其标准输入读取数据。通常,正被调用的程序并不会知道他正在由另一个进程读取数据;他只是简单的读取其标准输入流。

popen调用必须指明是"r"或者是"w";在popen的标准实现中并不支持其他的选项。这就意味着我们不能调用另一个程序,同时由其读取并向其写入。一旦失败,popen就会返回一个空指针。如果我们希望使用管道进行双向通信,通常的解决方法是使用两个管道,每个方向用于一个数据流。

pclose

当由popen所启动的进程已经完成时,我们可以使用pclose来关闭与其相关的文件流。pclose调用只会在由popen所启动的进程完成时才会返回。如果当调用pclose时,这个进程仍在运行,pclose调用就会等待这个进程结束。

pclose调用通常返回他正关闭的进程的文件流的返回的代码。如果调用在调用pclose之前已经执行了一个wait语句,返回状态就会丢失,而pclose就会返回-1,并且将errno设置为ECHILD。

试验--读取另一个程序的输出

下面我们来试验一个简单的popen与pclose的例子,popen1.c。我们会在一个程序中使用popen来访问uname所输出的信息。uname -a命令输出系统信息,包括机器类型,OS名字,版本号以及机器的网络名。

在初始化程序之后,我们打开一个连接uname的管道,使其可读并且设置read_fp指向输出。最后,由read_fp指向的管道会被关闭。

#include
#include
#include
#include

int main()
{
    FILE *read_fp;
    char buffer[BUFSIZ +1];
    int chars_read;
    memset(buffer,'\0',sizeof(buffer));
    read_fp = popen("uname -a","r");
    if(read_fp != NULL)
    {
        chars_read = fread(buffer,sizeof(char),BUFSIZ,read_fp);
        if(chars_read > 0)
        {
            printf("Output was:-\n%s\n",buffer);
        }
        pclose(read_fp);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
}

当我们运行这个程序,我们就会得到下面的输出:

$ ./popen1
Output was:-
Linux gw1 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux

工作原理

这个程序使用popen调用来调用带有-a参数的uname命令。他然后使用所返回的文件流来读取直到BUFSIZ个字符然后输出到屏幕上。因为我们在一个程序内部捕获uname输出的,所以他可以用来处理。

向popen发送输出

现在我们已经看到由一个外部程序捕获输出的例子,下面我们来看一下向另一个程序发送输出。这就是popen2.c,将数据导向另一个管道。在这里我们将使用od(octal dump)。

试验--向外部程序发送输出

看一下下面的代码,如果我们愿意可以将其输入文本编辑器。

#include
#include
#include
#include

int main()
{
    FILE *write_fp;
    char buffer[BUFSIZ +1];

    sprintf(buffer,"Once upon a time, there was...\n");
    write_fp = popen("od -c","w");
    if(write_fp != NULL)
    {
        fwrite(buffer,sizeof(char),strlen(buffer),write_fp);
        pclose(write_fp);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
}

当我们运行这个程序时,我们会得到下面的输出:

$ ./popen2
0000000   O n c e   u p o n   a   t i  m e
0000020   ,   t h e r e   w a s . . . \n
0000037

工作原理

这个程序使用带有"w"参数的popen调用来启动od -c命令,从而他可以向这个命令发送数据。然后他发送一个字符串,od -c命令可以接收并处理;od -c命令然后在其标准输出上输出其处理结果。

在命令行上,我们可以使用下面的命令来得到同样的结果

$ echo “Once upon a time, there was...” | od -c
阅读(720) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~