标准I/O和重定向的几个概念3标准文件描述符所有的Unix工具都使用文件描述符0、1、2。如下图所示,标准输入文件描述符为0,标准输出文件描述符为1,标准错误输出文件描述符为2。Unix假定文件描述符0、1和2已分别打开用于读、写和写操作。重定向I/O的是shell,而不是程序通过使用输出重定向标志,命令cmd>filename告诉shell将文件描述符1定位到文件。然后shell将文件描述符连接到指定的文件。该程序一直将数据写入文件描述符1,而没有意识到数据的目的地已更改。listargs.c显示该程序甚至没有在命令行上看到重定向符号。#includeintmain(intac,char*av[]){inti;printf("Numberofargs:%d,Argsare:\n",ac);for(i=0;ixyzonetwo2>oops也可以写成./listargs>xyztestingonetwo2>oops,如下图所示。***可用文件描述符(Lowest-Available-fd)原理文件描述符是一个索引号数组。每个进程都将其打开的文件集保存在一个数组中。文件描述符是该数组中文件的索引。并且,当打开文件时,为该文件分配的文件描述符始终是该数组中最新可用位置的索引。将标准输入重定向到文件考虑如何重定向标准输入,以便可以从文件中读取数据。更准确的说,进程不是从文件中读取数据,而是从文件描述符中读取数据。如果文件描述符0被重定向到一个文件,那么该文件将成为标准输入的源。方法一:先关后开第一种方法是先关后开策略,具体步骤如下:开始时,系统采用典型设置,即终端连接三个标准流的装置。输入流通过文件描述符0,输出流通过文件描述符1和2。接下来,调用close(0)以断开标准输入与终端设备的连接。***,使用open(filename,O_RDONLY)打开要连接到标准输入的文件。当前可用的文件描述符为0,因此打开的文件将连接到标准输入。任何从标准输入读取的函数都将从该文件中读取。方法二:open-close-dup-closeUnix系统调用dup建立第二个连接,指向一个已经存在的文件描述符。此方法需要4个步骤。打开(文件),打开标准输入将被重定向到的文件。这个调用返回一个文件描述符fd,它不是0,因为0当前是打开的。close(0),关闭文件描述符0,现在文件描述符0是空闲的。dup(fd),系统调用dup(fd)来复制文件描述符fd。使用最新的可用文件描述符在此处转载。所以得到的文件描述符为0。这样磁盘文件就连接了文件描述符0。close(fd),用close(fd)关闭原来的连接,只留下文件描述符0的连接。dup很学习管道时很重要。一个更简单的解决方案是将close(0)和dup(fd)合并为一个系统调用dup2。重定向I/O:who>userlist当您输入who>userlist时,shell运行who程序并将who的标准输出重定向到一个名为userlist的文件。shell实现这种重定向的关键在于fork和exec的时间间隔。fork执行完后,子进程还在运行父进程,也就是shell程序,准备执行exec。exec会替换进程中运行的程序,但不会改变进程的属性和进程中的所有连接。也就是说,运行exec之后,进程的用户ID没有改变,优先级没有改变,文件描述符与运行exec之前一样。因此,利用这个原理来实现标准输出的重定向。这个时候子进程要执行的命令是谁。在执行fork之前,父进程的文件描述符1指向终端。执行fork后,子进程的文件描述符也喜欢指向终端。这时子进程尝试执行close(1)。close(1)后,文件描述符1变成未使用的文件描述符,子进程现在执行creat(userlist,mode)打开文件userlist,文件描述符1连接到文件userlist。因此,子进程的标准输出被重定向到文件userlist,子进程再调用exec执行who。子进程执行who程序,所以子进程中的代码和数据被who程序的代码和数据替换,但文件描述符被保留。因为打开的文件不是程序代码或数据,它们是进程的属性,所以exec调用不会改变它们。管道编程管道是内核中的一种单向数据通道。管道有一个读端和一个写端,可用于将一个进程的输出连接到另一个进程的输入。创建管道使用系统调用result=pipe(intarray[2])创建管道并将其末端连接到两个文件描述符。如下图,array[0]是读数据的文件描述符,array[1]是写数据的文件描述符。与open调用类似,pipe调用也使用最新的可用文件描述符。程序pipedemo.c展示了如何创建管道并使用它向自身发送数据。核心代码如下:intlen,i,apipe[2];charbuf[BUFSIZ];if(pipe(apipe)==-1){perror("couldnotmakepipe.");exit(1);}printf("Gotapipe!Itisfiledescriptors:{%d%d}\n",apipe[0],apipe[1]);while(fgets(buf,BUFSIZ,stdin)){len=strlen(buf);if(write(apipe[1],buf,len)!=len){perror("writingtopipe.");break;}for(i=0;i