在看 unix 环境高级编程 的时候,在管道这一部分,还没有看到后面的代码时,一直被一个问题困扰着。先看代码

//15-6
#include "apue.h"
#include <sys/wait.h>#define  DEF_PAGER   "/bin/more"       /* default pager program */int
main(int argc, char *argv[])
{int        n;int       fd[2];pid_t pid;char    *pager, *argv0;char line[MAXLINE];FILE  *fp;if (argc != 2)err_quit("usage: a.out <pathname>");if ((fp = fopen(argv[1], "r")) == NULL)err_sys("can't open %s", argv[1]);if (pipe(fd) < 0)err_sys("pipe error");if ((pid = fork()) < 0) {err_sys("fork error");} else if (pid > 0) {                               /* parent */close(fd[0]);       /* close read end *//* parent copies argv[1] to pipe */while (fgets(line, MAXLINE, fp) != NULL) {n = strlen(line);if (write(fd[1], line, n) != n)            //这里只是把 line 中的字符串写入到管道的写端。还是很正常的err_sys("write error to pipe");}if (ferror(fp))err_sys("fgets error");close(fd[1]);    /* close write end of pipe for reader */if (waitpid(pid, NULL, 0) < 0)err_sys("waitpid error");exit(0);} else {                                        /* child */close(fd[1]);    /* close write end */if (fd[0] != STDIN_FILENO) {if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)       //在这里呢,则是把 标准输入 文件描述符重新指向了管道的读端,也是可以理解的err_sys("dup2 error to stdin");close(fd[0]); /* don't need this after dup2 */}/* get arguments for execl() *///这里我在 lbuntu 中测试的时候,是返回 NULL 的,所以最后 pager = "/bin/more",就是说下面 execl 调用的是 /bin/more 程序//至于 argv0 就是 "more" 字符串了,没啥好说的if ((pager = getenv("PAGER")) == NULL)pager = DEF_PAGER;              if ((argv0 = strrchr(pager, '/')) != NULL)argv0++;        /* step past rightmost slash */elseargv0 = pager;  /* no slash in pager */if (execl(pager, argv0, (char *)0) < 0)       //可是在这里,execl 一个程序后,为什么在父进程中写入到管道写端的字符串会被直接输出到了 标准输出 了呢?err_sys("execl error for %s", pager);}exit(0);
}

上面代码中最大的疑惑是:dup2的功能只是把 STDIN_FILENO 文件描述符重定向到 fd[0],可是在 excel 后,却可以在标准输出中输出了管道读端的内容。(难道标准输入也可以进行标准输出?或者 excel 后,程序会自动读取管道的内容?excel 后,管道会的内容会被自动读取出来?可是都不应该啊,从来就没有自动的东西)怎么也说不通。

继续看后面 15-12 ,15-12是作者实现的 popen() pclose() 函数。从中可以看到很多的细节。分析一下 popen() 的一部分代码:

//15-12
if ((pid = fork()) < 0) {return(NULL);  /* errno set by fork() */
} else if (pid == 0) {                            /* child */if (*type == 'r') {                  //如果标志是 'r',则把 标准输出 文件描述符重定向到管道的写端close(pfd[0]);if (pfd[1] != STDOUT_FILENO) {dup2(pfd[1], STDOUT_FILENO);close(pfd[1]);}} else {                        //如果标志是 'w',则把 标准输入 文件描述符重定向到管道的读端close(pfd[1]);if (pfd[0] != STDIN_FILENO) {dup2(pfd[0], STDIN_FILENO);close(pfd[0]);}}/* close all descriptors in childpid[] */for (i = 0; i < maxfd; i++)if (childpid[i] > 0)close(i);execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);_exit(127);
}/* parent continues... */
if (*type == 'r') {close(pfd[1]);if ((fp = fdopen(pfd[0], type)) == NULL)        //父进程把自己进程管道的读端生成流return(NULL);
} else {close(pfd[0]);if ((fp = fdopen(pfd[1], type)) == NULL)       //父进程把自己进程管道的写端生成流return(NULL);
}

假如,调用 popen() 的标志是 "r" ,那么在调用进程中的管道就是把 写端 给关闭了,然后把 读端生成了流。子进程呢,则是把 读端 关闭了,把 标准输出文件描述符 重定向到了 管道 的写端。

先大概了解这么多,再来看 15-14 和 15-15

//15-14
#include "apue.h"
#include <ctype.h>int
main(void)
{int        c;while ((c = getchar()) != EOF) {        //从标准输入读取数据if (isupper(c))c = tolower(c);if (putchar(c) == EOF)          //输出到标准输出err_sys("output error");if (c == '\n')fflush(stdout);            //冲洗流}exit(0);
}

15-14 最后会生成一个 myuclc 程序,被15-15调用

//15-15
#include "apue.h"
#include <sys/wait.h>int
main(void)
{char   line[MAXLINE];FILE  *fpin;if ((fpin = popen("myuclc", "r")) == NULL)     //使用 popen 调用 myuclc 程序err_sys("popen error");for ( ; ; ) {fputs("prompt> ", stdout);            //输出到标准输出fflush(stdout);if (fgets(line, MAXLINE, fpin) == NULL)       //从 fpin 流读入数据 需要讲解的就是这一部分了break;if (fputs(line, stdout) == EOF)          //在输出到标准输入err_sys("fputs error to pipe");}if (pclose(fpin) == -1)err_sys("pclose error");putchar('\n');exit(0);
}

从 fpin 流读入数据究竟是一个怎样的流程呢?

假设 15-15 的 main 为进程 A ,在调用 popen 时,就是 15-12 的代码,生成了子线程 B ,因为使用的是 “r” 标志,所以进程 A 就把自己管道的 写端 给关闭了,然后把 读端 生成了流。进程 B 则是把 读端 关闭了,把 标准输出 文件描述符 重定向到了 管道 的写端;然后进程 B 再调用 execl() ,进入到了程序 myuclc (15-14) 中。所以在程序 myuclc 中,它调用 标准输出 时,就会因为重定向的关系,数据就被写入到了管道的写端中;而 标准输入没有重定向,所以依旧会从控制终端上读取信息。

所以在进程 A 中,fputs() 仍旧使用标准输出,输出到控制终端

fgets(line,MAXLINE,fpin) 调用时,是从流 fpin 中读取数据,因为 fpin 流是由管道的读端生成的,所以就是从管道的读端读取数据。而管道的写端的数据哪里来呢?就是在程序 myuclc 中,调用标准输出的时候,重定向写入的了。

所以,完全没有上面疑惑的那些问题什么 excel 的自动化功能!!!!!!都是假的!!!!!全是手动的!!!!!至于为什么15-6中的程序,调用 /bin/more 能把文件的内容直接输出到了控制终端,那绝对是因为 more 程序通过标准输入(已经被重定向到管道的读端了)读取读端的内容,然后再偷偷的通过标准输出写出来的!

关于 dup2(fd[0],STDOUT_FILENO) 的一些波折相关推荐

  1. Linux之fd与dup2复制fd用法

    文件描述符fd 函数说明:   dup() 用来复制参数 oldfd 所指的文件描述符,并将它返回.此新的文件描述符和参数oldfd指的是同一个文件,共享所有的锁定.读写位置和各项权限或旗标.例如,当 ...

  2. linux fd dup 使用

    转自:http://mickole.blog.51cto.com/7547481/1246752  linux系统编程之文件与I0:文件描述符相关操作-dup,dup2,fcntl 2013-07-1 ...

  3. linux Dup2 网络转发,Linux系统编程:dup2()重定向

    对于Dup2 的理解: 源代码: 1 #include 2 #include 3 #include 4 #include 5 #include 6 7 #define MSGSIZE 20 8 #de ...

  4. Android 8.1 源码_启动篇(一) -- 深入研究 init(转 Android 9.0 分析)

    前言 init进程,它是一个由内核启动的用户级进程,当Linux内核启动之后,运行的第一个进程是init,这个进程是一个守护进程,确切的说,它是Linux系统中用户控件的第一个进程,所以它的进程号是1 ...

  5. 用指针的观点来深入理解dup和dup2的用法

    用指针的观点来深入理解dup和dup2的用法 作者:ejian 2007-09-17 网上有很多资料详细讲解了dup和dup2的用法,有讲得不错的,但总感觉理解起来还是有点困难,通过实践,我发现一种C ...

  6. 【Linux系统编程】文件描述符的复制dup()和dup2()

    00. 目录 文章目录 00. 目录 01. 文件描述符复制概述 02. 常用函数 2.1 dup函数 2.2 dup2函数 03. 案例实战 3.1 dup示例 3.2 dup2示例 04. 附录 ...

  7. Android8.0 系统启动之孵化准备

    在帕美尔省后面有一座金刚山,它有一小时路程高,一小时路程宽,一小时路程深.有一只鸟每隔一百年飞到这里来一次,在这座山上磨一磨它的小嘴.要等到这座山完全磨平,那么永恒的第一秒钟才算过去. ------- ...

  8. Android7.0 init进程源码分析

    目的 看过一些blog和相关的书籍,大多数文章在介绍init进程时,参考的代码比较久远,同时不同文章行文的重点不太一样,因此决定自己试着来分析一下,并作相应的记录. 背景 当linux内核启动之后,运 ...

  9. 【Linux系统编程:基础IO 下】dup2 实现输出重定向、输入重定向、追加重定向 | 理解磁盘 | 理解文件系统中inode的概念 | 软硬链接

    写在前面 这里先接着<基础IO 上>中的缓冲区的内容作些补充,这里主要补充 dup2 接口. ✔ 测试用例一: #include<stdio.h> #include<sy ...

最新文章

  1. 肢解“文件夹图标”病毒,制作专杀工具——“郑大扫帚”
  2. a 中调用js的几种方法整理及使用推荐
  3. 【总结】MTO/MTS操作步骤及月末结算
  4. 如何设置mysql的权限_mysql 权限控制
  5. javaee 第五周作业
  6. java中访问控制修饰符什么含义_关于Java中访问控制修饰符的来由及浅解
  7. Oracle01877,Cognos错误:RQP-DEF-0177 执行操作“sqlOpenResult”(状态为“-28”)时出错...
  8. Jquery获取iframe中的元素
  9. Java SE 8新特性
  10. 第7章 PCA与梯度上升法 学习笔记中
  11. AVPlayerViewController支持横屏设置
  12. 树莓派安装python3.5_一树 - 神奇宝贝百科,关于宝可梦的百科全书
  13. 【vim】你的背包里,缺不缺一份vim简明教程嘞?
  14. qt 串口助手 界面美化
  15. 研发效能度量指标及其如何度量
  16. 空间大地测量与GPS导航定位时间系统相互转换,格里高利时通用时儒略日,GPS时,年积日相互转换
  17. SpringBoot后台搭建-创建restful接口,使用mybatisPlus实现分页
  18. [BZOJ 1193] 马步距离
  19. 量子Fourier变换笔记
  20. 景区夜游如何为游客讲好文旅故事

热门文章

  1. SDH与MDH参数法对比
  2. 小木乃伊到我家(spfa算法)
  3. 如何打破传统线下进货渠道劣势?
  4. 简单 Python 快乐之旅之:Python 基础语法之文件操作专题
  5. 《MySQL必修课:存储引擎大揭秘!InnoDB和MyISAM究竟谁更强?》
  6. 微信小程序支付统一下单接口and异步回调
  7. 读 TiDB 论文有感 | 数据强一致性且资源隔离的 HTAP 数据库
  8. 经典英文电影台词~有必要都背下来,英语会很强悍~
  9. java一般项目提成多少_java面试之经典提成问题
  10. Q-Q图原理及Python实现