在Unix系统中,每个进程都有STDIN、STDOUT和STDERR这3种标准I/O,它们是程序最通用的输入输出方式。几乎所有语言都有相应的标准I/O函数,比如,C语言可以通过scanf从终端输入字符,通过printf向终端输出字符。熟悉Shell的朋友都知道,我们可以方便地对Shell命令进行I/O重定向,比如 find -name "*.java" >testfile.txt 把当前目录下的Java文件列表重定向到testfile.txt。多数情况下,我们只需要了解I/O重定向的使用就够了,但是如果要编程实现类似Shell的I/O重定向以及管道功能,那么就需要清楚它的原理和实现。

下面本文就以Linux系统为具体例子,介绍I/O重定向的原理和实现(文中实验环境为Ubuntu 12.04,内核版本3.2.0-59)。

文件描述符表

理解I/O重定向的原理需要从Linux内核为进程所维护的关键数据结构入手。对Linux进程来讲,每个打开的文件都是通过文件描述符(File Descriptor)来标识的,内核为每个进程维护了一个文件描述符表,这个表以FD为索引,再进一步指向文件的详细信息。在进程创建时,内核为进程默认创建了0、1、2三个特殊的FD,这就是STDIN、STDOUT和STDERR,如下图所示意:

所谓的I/O重定向也就是让已创建的FD指向其他文件。比如,下面是对STDOUT重定向到testfile.txt前后内核文件描述符表变化的示意图

重定向前:

重定向后:

在I/O重定向的过程中,不变的是FD 0/1/2代表STDIN/STDOUT/STDERR,变化的是文件描述符表中FD 0/1/2对应的具体文件,应用程序只关心前者。本质上这和接口的原理是相通的,通过一个间接层把功能的使用者和提供者解耦。

下面我们通过strace命令跟踪一下echo命令的系统调用:

dagang@ubuntu12:~$ strace echo hello 2>&1 >/dev/null | grep write
write(1, "hello\n", 6) = 6

我们可以看到 write(1, "hello\n", 6) 这样一个系统调用,它的第一个参数1就是代表的STDOUT的FD,这说明对于echo程序,它只管(通过标准I/O函数从STDOUT)向FD 1写入,而不关心它们FD 1到底对应的是哪个文件。

Shell正是通过I/O重定向和管道这种特殊的文件把多个程序的STDIN和STDOUT串联在一起组成更复杂功能的,下面是Shell中通过管道的示意图:

下面我们用一个实际的例子来体验一下:

dagang@ubuntu12:~$ sleep 30 | sleep 40 &
[1] 5584
dagang@ubuntu12:~$ pgrep -l sleep
5583 sleep
5584 sleep
dagang@ubuntu12:~$ ll /proc/5583/fd
total 0
lrwx------ 1 dagang dagang 64 Feb 27 13:41 0 -> /dev/pts/3
l-wx------ 1 dagang dagang 64 Feb 27 13:41 1 -> pipe:[246469]
lrwx------ 1 dagang dagang 64 Feb 27 13:41 2 -> /dev/pts/3
dagang@ubuntu12:~$ ll /proc/5584/fd
total 0
lr-x------ 1 dagang dagang 64 Feb 27 13:41 0 -> pipe:[246469]
lrwx------ 1 dagang dagang 64 Feb 27 13:41 1 -> /dev/pts/3
lrwx------ 1 dagang dagang 64 Feb 27 13:41 2 -> /dev/pts/3

上面我们启动了两个进程5583和5584,通过查看/proc//fd,我们看到进程5583的STDOUT和5584的STDIN被重定向到了pipe:[246469],这样就达到了连接两个进程标准I/O的目的。

dup2()系统调用

上面介绍了文件描述符表和I/O重定向的原理,那么在Linux系统中如何通过C程序实现I/O重定向呢?主要用到了dup2()这个系统调用,man中关于dup2是这样说的:

int dup2(int oldfd, int newfd);

dup2() create a copy of the file descriptor oldfd. After a successful return from dup() or dup2(), the old and new file descriptors may be used interchangeably. They refer to the same open file description (see open(2)) and thus share file offset and file status flags; for example, if the file offset is modified by using lseek(2) on one of the descriptors, the offset is also changed for the other.

这里我们通过一个实际的问题来说明它的使用方法:

编写一个C程序,通过调用sort这个Shell命令进行排序,要求把in.txt和out.txt分别重定向到sort的STDIN,STDOUT。

参考实现:

int main() {
int pid = 0;
// fork a worker process
if (pid = fork()) {
// wait for completion of the child process
int status;
waitpid(pid, &status, 0);
}
else {
// open input and output files
int fd_in = open("in.txt", O_RDONLY);
int fd_out = open("out.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd_in > 0 && fd_out > 0) {
// redirect STDIN/STDOUT for this process
dup2(fd_in, 0);
dup2(fd_out, 1);
// call shell command
system("sort");
close(fd_in);
close(fd_out);
}
else {
// ... error handling
}
}
return 0;
}

上面的主要步骤包括:

1. 首先fork一个子进程,后续步骤都在子进程中完成,父进程通过waitpid()系统调用等待子进程结束;
2. 打开open()系统调用打开in.txt和out.txt,得到它们的描述符(在我的测试中,这两个值通常为3和4);
3. 通过dup2()系统调用把STDIN重定向到fd_in,把STDOUT重定向到fd_out(注意,重定向的影响范围是整个子进程);
4. 通过system()系统调用运行shell命令sort

通过上面的例子我们就了解最基本的I/O重定向的实现方法,接下来,你能否根据这些知识进一步实现出Shell的管道特性呢?

总结

本文介绍了Linux系统I/O重定向的原理和实现方式。原理方法最重要的是理解文件描述符和文件描述符表的概念,以及标准I/O所对应的特殊FD;实现方面主要是了解dup2()系统调用的功能和用法。最后需要注意的是dup2()不仅可以用来对标准I/O重定向,对任何FD都是可以的,这是习惯使用Shell进行标准I/O重定向的朋友容易忽略的。

图片引用

http://cs.ucla.edu/classes/fall08/cs111/scribe/4/FDT_diagram.JPG
http://academic.udayton.edu/SaverioPerugini/courses/cps346/lecture_notes/images/beforeredir.png
http://academic.udayton.edu/SaverioPerugini/courses/cps346/lecture_notes/images/afterredir.png
http://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Pipeline.svg/280px-Pipeline.svg.png

I/O重定向的原理和实现的更多相关文章

  1. 请求转发和URL重定向的原理和区别

    一.请求转发和重定向是在java后台servlet中,由一个servlet跳转到另一个servlet/jsp要使用的技术 使用方法 请求转发  req.getResquestDispatcher(se ...

  2. SpringMVC中重定向底层原理

      只要将数据放入model中, 也能取到值,原因是model临时放入session域中,当从定向到另一个url时,底层把数据拼接在url地址后面(重定向一定是get请求方式),同时将session域 ...

  3. http协议 301和302的原理及实现

    一.来看看官方的说法: 301,302 都是HTTP状态的编码,都代表着某个URL发生了转移,不同之处在于:  301 redirect: 301 代表永久性转移(Permanently Moved) ...

  4. (转)Windows管道(Pipe)重定向stdout,stderr,stdin

    参考: http://qiusuoge.com/11496.html http://www.cnblogs.com/BoyXiao/archive/2011/01/01/1923828.html st ...

  5. 请求转发 和 URL 重定向

    五 请求转发 和 URL 重定向 1 请求转发和重定向 干什么用? 是我们在java后台servlet中 由一个servlet跳转到 另一个 servlet/jsp 要使用的技术 前端发送请求到后台 ...

  6. shell编程 之 输入输出重定向

    1 输入输出重定向 标准输入:从终端得到命令,对于计算机来说,是从终端获得了命令,执行完了以后,结果和执行状态或者错误提示又会发回终端,这叫标准输出. 输入输出重定向就是从终端以外的别的地方得到输入, ...

  7. SpringMVC 重定向和请求转发(转载)

    本文系转载,原文地址:https://blog.csdn.net/m0_37450089/article/details/78703366   servlet的请求转发(forward)和重定向(se ...

  8. Servlet-转发和重定向的区别

    实际发生位置不同,地址栏不同 转发是发生在服务器上的 转发是由服务器进行跳转的,细心的朋友会发现,在转发的时候,浏览器的地址栏是没有发生变化的,在我访问Servlet111的时候,即使跳转到了Serv ...

  9. springMVC之一(页面<--->控制器 互相传值,转发和重定向)

    #页面--->控制器1.request:不建议使用2.使用属性传值(建议使用)@RequestParam("name") String username3.使用Bean对象传 ...

随机推荐

  1. tomcat热部署

    tomcat默认支持热部署,修改文件会自动加载部署,不需要重启容器 server.xml配置如下所示 autoDeploy=”true” — 自动部署 reloadable=”true” — 自动加载

  2. 获取 windows 商店内的 aapx 安装包 并 安装(旁加载)

    这是一篇教程,写在 win10 版<量子破碎>发售近期. 主要原因:windows 商城的应用下载实在难以忍受...... #######2016-4-9更新####### 4-6号通过旁 ...

  3. 查看Linux系统版本与位数

    查看系统发行版信息 查看LSB (Linux Standard Base)本身的版本信息. .el5   .el5 .el5 -bit LSB executable, Intel 80386, ver ...

  4. 贝塞尔曲线(cubic bezier)

    对于css3的Transitions,网上很多介绍,相信大家都比较了解,这里用最简单的方式介绍下: transition语法:transition:<transition-property> ...

  5. 杨氏矩阵定义及其查找的实现C++

    先介绍一下这个数据结构的定义,Young Tableau有一个m*n的矩阵,然后有一数组 a[k], 其中 k<=m*n ,然后把a[k]中的数填入 m*n 的矩阵中,填充规则为: 1.  每一 ...

  6. kafka的log存储解析——topic的分区partition分段segment以及索引等

    转自:http://blog.csdn.net/jewes/article/details/42970799 引言 Kafka中的Message是以topic为基本单位组织的,不同的topic之间是相 ...

  7. windows 10 开始菜单和cortana无法工作的问题

    过了个周末,到了实验室一开机发现报了个关键错误:开始菜单和cortana无法工作. 经过一番google ,发现问题,原来是360禁用了一个服务导致,这个服务是UserManager. 我直接去开启发 ...

  8. S3C2440UART之FIFO

    一.基础知识 S3C2440有3个独立的串口,每一个都可以利用DMA和中断方式操作.每个包含2个64字节FIFO,一个收,一个发.非FIFO模式相当于FIFO模式的一个寄存器缓冲模式.每一个UART有 ...

  9. C++ activemq CMS 学习笔记.

    很早前就仓促的接触过activemq,但当时太赶时间.后面发现activemq 需要了解的东西实在是太多了. 关于activemq 一直想起一遍文章.但也一直缺少自己的见解.或许是网上这些文章太多了. ...

  10. python 虚拟环境

    python3 目录venv创建为虚拟环境,并激活. $ python3 -m venv ./venv$ source venv/bin/activate -m: -m mod : run libra ...