I/O重定向的原理和实现


在Unix系统中,每个进程都有STDIN、STDOUT和STDERR这3种标准I/O,它们是程序最通用的输入输出方式。

在日常操作中,我们很习惯的用上了重定向和管道( 1|2 :1的输出作为2的输入)操作,那么究竟原理是怎样的呢?

   我们知道每个进程都会维护一个文件描述符表(FILE  DESCRIPTOR  TABLE),用以定位被打开的文件。

   实际上,表中的记录(文件描述符)是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表

   在进程创建时,内核为进程默认创建了0、1、2三个特殊的FD,这就是STDIN、STDOUT和STDERR。

   当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。

接下去我们要从文件描述符展开介绍重定位和管道是怎么一回事,先看一下文件描述符表的示意图。


很清晰的可以看出来之前描述的情况,接着我们研究重定向,用 ls /bin  > testfile.txt为例。



我们便理解了,所谓的I/O重定向也就是让已创建的FD指向其他文件(testfile.txt)

文件描述符 0/1/2代表STDIN/STDOUT/STDERR代表的意义本身没有变化,变化的是文件描述符表中FD 0/1/2对应的具体文件,

从这个描述符表中我们得到的信息表示   ls /bin 的标准输出STDOUT重定向到了textfile.txt

这里强调一下文件描述符是一个索引号,每个表项都指向已打开文件的指针。

默认的STDIN/STDOUT/STDERR指向的是 /dev/ttyX (X表示用哪个终端执行的程序的号码)


接着看一个管道的例子(摘录自某篇博客)

crazy@ubuntu14:~$ sleep 30 | sleep 40 &
[1] 5584
crazy@ubuntu14:~$ pgrep -l sleep
5583 sleep
5584 sleep
crazy@ubuntu14:~$ ll /proc/5583/fd
total 0lrwx------ 1 crazy crazy

64 Feb 27 13:41 0 -> /dev/pts/3l-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
crazy@ubuntu14:~$ ll /proc/5584/fd
total 0lr-x------ 1 crazy crazy 64 Feb 27 13:41 0 -> pipe:[246469]
lrwx------ 1 crazy crazy 64 Feb 27 13:41 1 -> /dev/pts/3lrwx------ 1 crazy crazy 64 Feb 27 13:41 2 -> /dev/pts/3

上面我们启动了两个进程5583和5584,通过查看/proc//fd,

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


接着我们讲一下实际运用重定位的操作

—————————————————————————————————————————————

  cmd > file                    把 stdout 重定向到 file 文件中
  cmd >> file                   把 stdout 重定向到 file 文件中(追加)

  cmd 2> file                   把 stderr 重定向到 file 文件中
  cmd 2>> file                  把 stderr 重定向到 file 文件中(追加)

  cmd < file                    cmd 命令以 file 文件作为 stdin

  cat <>file                    以读写的方式打开 file

  cmd < file >file2             cmd 命令以 file 文件作为 stdin,以 file2 文件作为 stdout
  cmd > file 2>&1               把 stdout 和 stderr 一起重定向到 file 文件中 (2重定向到与描述符1指向文件)
  cmd >> file 2>&1              把 stderr 和 stderr 一起重定向到 file 文件中 (追加方式)

  cmd << delimiter              Here document,从 stdin 中读入,直至遇到delimiter 分界符

————————————————————————————————————————————————

  >&n                使用系统调用 dup (2) 复制文件描述符 n 并把结果用作标准输出
  <&n                标准输入复制自文件描述符 n
  <&-                关闭标准输入(键盘)
  >&-                关闭标准输出
  n<&-               表示将 n 号输入关闭
  n>&-               表示将 n 号输出关闭

—————————————————————————————————————————————————

上面介绍了文件描述符表和I/O重定向的原理,那么在Linux系统中如何通过C程序实现I/O重定向呢?

主要是用这个函数  int dup2(int oldfd, int newfd);

int main() {
    int pid = 0;
    // fork a worker process
    if (pid = fork()) { //father process
        // wait for completion of the child process
        int status; 
        waitpid(pid, &status, 0);
    }
    else { //child process
        // 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);
        }
    }
    return 0;
}

上面的主要步骤包括:

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









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值