Unix系统编程()复制文件描述符

时间:2022-01-30 10:00:20

Bourne shell的IO冲重定向语法2>&1,意在通知shell把标准错误(文件描述符2)重定向到标准输出(文件描述符1)。因此下列命令将把标准输出和标准错误写入result.log文件。(shell按从左至右的顺序处理IO重定向语句)

 

shell通过复制文件描述符2实现了标准错误的重定向操作,因此文件描述2与文件描述符1指向同一个打开文件的句柄。可以通过调用dup和dup2来实现此功能。

 

对于这种重定向的操作,仅仅打开一个目标文件两次是远远不够的(第一次在描述符1上打开,第二次在描述符2上打开)。

首先两个文件不能共享文件偏移量指针,因此有可能导致相互覆盖彼此的输出。

再者打开的文件不一定就是磁盘文件。

在如下命令中,标准错误就将和标准输出一起送达同一管道。

 

$ ./myscript 2>&1 | less

 

dup调用复制一个打开的文件描述符oldfd,并返回一个新的描述符,二者都指向同一打开的文件句柄。系统会保证新文件描述符一定是编号值最低的未使用文件描述符。

 

int dup(int oldfd);

 

加入发起如下调用:

newfd = dup(1);

 

再假定在正常的情况下,shell已经代表程序打开了文件描述符0、1和2,且没有其他描述符在用,dup调用会创建文件描述符1的副本,返回的文件描述符编号值为3。

 

如果希望返回的文件描述符为2,可以这样:

close(2);

newfd = dup(1);

 

只有当描述符0已经打开的时候,这段代码方可工作。如果想进一步简化如下的代码,同时希望获得所期望的文件描述符,可以调用dup2。

 

int dup2(int oldfd, int newfd);

 

dup2系统调用会为oldfd参数所指定的文件描述符创建副本,其编号由newfd参数指定。如果由newfd参数所指定的编号的文件描述符之前已经打开了,那么dup2会首先将其关闭。

 

dup2调用会默认忽略newfd关闭期间出现的任何错误。故此,编码时更为安全的做法是:在调用dup2之前,如果newfd已经打开,则应显式调用close将其关闭。(那要是我不知道newfd是否打开了怎么判断呢?)

 

之前调用dup和close可以简化为:

dup(1, 2);

 

如果dup2调用成功,则将返回副本的文件描述符编号(即newfd参数指定的值)。

 

如果oldfd并非有效的文件描述符,那么dup2调用将失败并返回错误EBADF,且不关闭newfd。如果oldfd有效,且与newfd的值相等,那么dup2将什么也不做,不关闭newfd,并将其作为调用结果返回。

 

fcntl的F_DUPFD操作是复制文件描述符的另一接口,更具有灵活性。

 

newfd = fcntl(oldfd, F_DUPFD, startfd);

 

该调用为oldfd创建一个副本,且将使用大于等于startfd的最小未使用值作为描述符编号。该调用还能保证新描述符newfd编号落在特定的区间范围内。总是能将dup和dup2的调用改写为对close和fcntl的调用。

 

文件描述符的正、副本之间共享打开文件句柄所含的文件偏移量和状态标志。然后新文件描述符有其自己的一套文件描述符标志,且其close-on-exec标志(FD_CLOEXEC)总处于关闭状态。

 

下面的接口可以直接控制新文件描述符的close-on-exec标志。

 

dup3系统调用完成的工作与dup2相同,只是新增了一个附加参数flag,这一个可以是修改系统调用行为的位掩码。

 

int dup3(int oldfd, int newfd, int flags);

目前dup3只支持一个标志O_CLOEXEC。

 

dup3只在Linux 2.6.27以后才有,是Linux特有的。

 

Linux从2.6.24开始支持fcntl用于复制文件描述符的附加命令:F_DUPFD_CLOEXEC。

 

该标志不仅实现了与F_DUPFD相同的功能,还为新文件描述符设置close-on-exec标志。

 

这一节有好多系统系统调用。

 

dup,dup2,dup3,fcntl