分享

进程间通信--管道

 心不留意外尘 2016-05-16

http://blog.csdn.net/deep_explore/article/details/6928530

2011

在网络程序设计中,如果没有进程间通信,那么软件的功能肯定会大打折扣。

进程间通信有哪些途径呢?

1.信号

2.管道

3.消息队列

4.信号量

5.共享内存


管道:

管道就是将一个程序的输出与另一个程序的输入连接起来的单向通道。它是UNIX/Linux中古老而最广泛的进程间通信的方式。特别是在shell中。

在C语言中我们用Pipe()函数来建立管道:


===============

系统调用: pipe();
函数声明: int pipe( int fd[2] );
返回值:     0 on success
                  -1 on error: errno = EMFILE (no free descriptors)
                                                     EMFILE (system file table is full)
                                                     EFAULT (fd array is not valid)
注意: fd[0] 用来从管道中读, fd[1] 用来向管道中写
数组中的第一个元素(fd[0])是从管道中读出数据的句柄,第二个元素(fd[1])是向
管道写入数据的句柄。也即是说,fd[1]的写入由 fd[0]读出。


使用pipe在子进程中读入数据,然后在父进程中读出数据的例子:
====================================================================

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
    int fd[2];
    pid_t childpid;
    int nbytes;
    char string[] = "Hello,world!\n";
    char readbuffer[80];

    pipe(fd);
    if((childpid = fork()) == -1){
        perror("fork error");
        exit(1);
    }
    if(childpid == 0){
        //close child process read pipe
        close(fd[0]);
        //write data by write pipe
        write(fd[1], string, strlen(string));
        printf("Sended string:%s", string);
        _exit(0);
    }else{
        //close parent process write pipe
        close(fd[1]);
        // read data by read pipe
        nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
        printf("Received string:%s", readbuffer);
    }
    return 0;
}


编译运行

==========

[root@explore code]# ./a.out
Received string:Hello,world!
Sended string:Hello,world!


>使用 dup()函数

有时候我们需要将子进程当中的管道的句柄定向到标准 I/O(stdin/stdout)上去。这样,
在子进程中使用 exec()函数调用外部程序时,这个外部程序就会将管道作为它的输入/输出。
这个过程可以用系统函数 dup()来实现。其函数原型如下:
系统调用: dup();
函数声明: int dup( int oldfd );
返回值:     new descriptor on success
                  -1 on error: errno = EBADF (oldfd is not a valid descriptor)
                                                     EBADF (newfd is out of range)
                                                     EMFILE (too many descriptors for the process)

虽然原句柄和新句柄是可以互换使用的,但为了避免混淆,我们通常会将原句柄关闭
(close)
。同时要注意,在 dup()函数中我们无法指定重定向的新句柄,系统将自动使用未
被使用的最小的文件句柄(记住,句柄是一个整型量)作为重定向的新句柄。请看下面的
例子:
......
......
pipe(fd);
childpid = fork();
if(childpid == 0)
{
/* 关闭子进程的文件句柄 0(stdin) */
close(0);
/* 将管道的读句柄定义到 stdin */
dup(fd[0]);
execlp(“
sort” “
, sort” NULL);
,
......
}


>使用 dup2()函数
在 Linux 系统中还有一个系统函数 dup2()。单从函数名上我们也可以判断出它和 dup()
函数的渊源。下面是它的原型:
系统调用: dup2();
函数声明: int dup2( int oldfd, int newfd );
返回值:     new descriptor on success
                  -1 on error: errno = EBADF (oldfd is not a valid descriptor)
                                                     EBADF (newfd is out of range)
                                                     EMFILE(too many descriptors for the process)

注意: 旧句柄将被 dup2()自动关闭

显然,原来的 close 以及 dup 这一套调用现在全部由 dup2()来完成。这样不仅简便了程
序,更重要的是,它保证了操作的独立性和完整性,不会被外来的信号所中断。在原来的
dup()调用中,我们必须先调用 close()函数。假设此时恰好一个信号使接下来的 dup()调用不
能立即执行,这就会引发错误(进程没有了 stdin )
。使用 dup2()就不会有这样的危险。下
面的例子演示了 dup2()函数的使用:
......
pipe(fd);
.
childpid = fork();
if(childpid == 0)
{
/* 将管道的读入端定向到 stdin */
dup2(0, fd[0]);
execlp("sort", "sort", NULL);
......
}

>使用 popen()/pclose()函数
看了 dup2()函数,一定有人会想,既然能把 close 和 dup 合成一个函数,那么有没有把
fork、exec 和 dup()结合的函数呢?答案是肯定的。它就是 linux 的系统函数 popen():
库函数: popen();
函数声明: FILE *popen ( char *command, char *type);
返回值: new file stream on success
NULL on unsuccessful fork() or pipe() call
NOTES: creates a pipe, and performs fork/exec operations using "command"
popen()函数首先调用 pipe()函数建立一个管道,然后它用 fork()函数建立一个子进程,
运行一个 shell 环境,然后在这个 shell 环境中运行"command"参数指定的程序。数据在管道
中流向由"type"参数控制。这个参数可以是"r"或者"w",分别代表读和写。需要注意的是,
"r"和"w"两个参数不能同时使用!在 Linux 系统中,popen 函数将只使用"type"参数中第一
个字符,也就是说,使用"rw"和"r"作为"type"参数的效果是一样的,管道将只打开成读状态。
使用 popen 打开的管道必须用 pclose()函数来关闭。还记得 fopen 和 fclose 的配对使用
吗?这里再次显示了管道和文件的相似性。
库函数: pclose();
函数声明: int pclose( FILE *stream );
返回值:
exit status of wait4() call
-1 if "stream" is not valid, or if wait4() fails
NOTES: waits on the pipe process to terminate, then closes the stream.


下面是popen和pclose的例子,但是在没有运行成功,情景如下:

#include<stdio.h>
#define MAXSTRS 5
int main()
{
    int cntr;
    FILE *pipe_fp;
    char *strings[MAXSTRS] = { "roy", "zixia", "gouki", "supper", "mmwan"};
    // create pipe with popen
    if((pipe_fp = popen("sort", "w")) == NULL ){
        perror("popen failed");
        exit(1);
    }
    // Processing loop
    for(cntr = 0; cntr < MAXSTRS; cntr++){
        fputs(strings[cntr], pipe_fp);
        fputs('\n', pipe_fp);
    }
    //close pipe
    pclose(pipe_fp);

    return 0;
}


编译运行

===========
[root@explore code]# gcc popen_pclose.c
popen_pclose.c: In function ‘main’:
popen_pclose.c:11:3: warning: incompatible implicit declaration of built-in function ‘exit’ [enabled by default]
popen_pclose.c:16:3: warning: passing argument 1 of ‘fputs’ makes pointer from integer without a cast [enabled by default]
/usr/include/stdio.h:684:12: note: expected ‘const char * __restrict__’ but argument is of type ‘int’
[root@explore code]# ./a.out
Segmentation fault (core dumped)

popen()\pclose()的例子

#include<stdio.h>
int main()
{
    FILE *pipein_fp, *pipeout_fp;
    char readbuffer[80];
    // create a pipe to "ls read pipe"
    if((pipein_fp = popen("ls", "r")) == NULL){
        perror("popen");
        exit(1);
    }
    // create a pipe to "sort write"
    if((pipeout_fp = popen("sort", "w")) == NULL){
        perror("popen");
        exit(1);
    }
    // Processing loop
    while(fgets(readbuffer, 80, pipein_fp)){
        fputs(readbuffer, pipeout_fp);
    }
    // close pipe
    pclose(pipein_fp);
    pclose(pipeout_fp);

    return 0;
}


编译运行
============
[root@explore code]# gcc popen_pclose_2.c
popen_pclose_2.c: In function ‘main’:
popen_pclose_2.c:9:3: warning: incompatible implicit declaration of built-in function ‘exit’ [enabled by default]
popen_pclose_2.c:14:3: warning: incompatible implicit declaration of built-in function ‘exit’ [enabled by default]
[root@explore code]# ./a.out
a.out
gentmp.c
pipe.c
popen_pclose_2.c
popen_pclose.c



以下是一些在管道的使用中需要注意的问题:
1. ipe()的调用必须在 fork()之前;
p
2.及时关闭不需要的管道句柄;
3.使用 dup()之前确定定向的目标是最小的文件句柄;
4.管道只能实现父子进程间的通信,如果两个进程之间没有 fork()关系,就必须考虑
其他的进程通信方法。




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多