#include<unistd.h>
int dup(int fd);
int dup2(int fd1,int fd2);两个均为 复制一个现存 的文件的描述
两个函数的返回:若成功为新的文件描述,若出错为-1;
由dup返回的新文件描述符一定是当前可用文件描述中的最小数值。用dup2则可以用fd2参数指定新的描述符数值。如果fd2已经打开,则先关闭。若fd1=fd2,则dup2返
回fd2,而不关闭它。通常使用这两个系统调用来重定向一个打开的文件描述符。
函数简介
函数名: dup
功 能: 复制一个文件句柄
用 法: int dup(int handle);
相关函数: dup2、fdopen、freopen、mbsdup、wcsdup、strdup
- #include <string.h>
- #include <stdio.h>
- void flush(FILE *stream);
- int main(void)
- {
- FILE *fp;
- char msg[] = "This is a test";
- /*create a file*/
- fp = fopen("DUMMY.FIL","w");
- /*write some date to the file*/
- fwrite(msg,strlen(msg),1,fp);
- printf("Press any key to flush DUMMY.FIL");
- getchar();
- /*flush the data to DUMMY.FIL without closing it*/
- flush(fp);
- printf("\n File was flushed,Press any key to quit:");
- getchar();
- return 0;
- }
- void flush(FILE *stream)
- {
- int duphandle;
- /*flush TC's internal buffer*/
- fflush(stream);
- /* make a duplicate file handle*/
- duphandle = dup(fileno(stream));
- /* close the duplicate handle to flush the DOS buffer*/
- close(DUPHANDle);
- }
一、单个进程内的dup和dup2
假设进程A拥有一个已打开的文件描述符fd3,它的状态如下
进程A的文件描述符表(before dup2)
- ------------
- fd0 0 | p0
- ------------
- fd1 1 | p1 -------------> 文件表1 ---------> vnode1
- ------------
- fd2 2 | p2
- ------------
- fd3 3 | p3 -------------> 文件表2 ---------> vnode2
- ------------
- ... ...
- ... ...
- ------------
经下面调用:
n_fd = dup2(fd3, STDOUT_FILENO);后进程状态如下:
进程A的文件描述符表(after dup2)
- ------------
- fd0 0 | p0
- ------------
- n_fd 1 | p1 ------------
- ------------ \
- fd2 2 | p2 \
- ------------ _\|
- fd3 3 | p3 -------------> 文件表2 ---------> vnode2
- ------------
- ... ...
- ... ...
- ------------
解释如下:n_fd = dup2(fd3, STDOUT_FILENO)表示n_fd与fd3共享一个文件表项(它们的文件表指针指向同一个文件表项),n_fd在文件描述符表中的位置为 STDOUT_FILENO的位置,而原先的STDOUT_FILENO所指向的文件表项被关闭,我觉得上图应该很清晰的反映出这点。按照上面的解释我们就可以解释CU中提出的一些问题:
(1) "dup2的第一个参数是不是必须为已打开的合法filedes?" -- 答案:必须。
(2) "dup2的第二个参数可以是任意合法范围的filedes值么?" -- 答案:可以,在Unix其取值区间为[0,255]。
(每一个进程都对应一个结构体 task_struct ,里面包含一个数据成员 :
- /* open file information */
- struct files_struct *files;
- /*
- * Open file table structure
- */
- struct files_struct {
- /*
- * read mostly part
- */
- atomic_t count;
- struct fdtable __rcu *fdt;
- struct fdtable fdtab;
- /*
- * written part on a separate cache line in SMP
- */
- spinlock_t file_lock ____cacheline_aligned_in_smp;
- int next_fd;
- struct embedded_fd_set close_on_exec_init;
- struct embedded_fd_set open_fds_init;
- struct file __rcu * fd_array[NR_OPEN_DEFAULT];
- };
二、重定向后恢复
CU上有这样一个帖子,就是如何在重定向后再恢复原来的状态?首先大家都能想到要保存重定向前的文件描述符。那么如何来保存呢,象下面这样行么?
int s_fd = STDOUT_FILENO;
int n_fd = dup2(fd3, STDOUT_FILENO);
还是这样可以呢?
int s_fd = dup(STDOUT_FILENO);
int n_fd = dup2(fd3, STDOUT_FILENO);
这两种方法的区别到底在哪呢?答案是第二种方案才是正确的,分析如下:按照第一种方法,我们仅仅在"表面上"保存了相当于fd_t(按照我前面说的理解方法)中的index,
而在调用dup2之后,ptr所指向的文件表项由于计数值已为零而被关闭了,我们如果再调用dup2(s_fd, fd3) (注意此时s_fd对应的文件表项已经关闭)就会出错。而第二种方法我们首先做一下复制(这样对应的文件表项不会被关闭),复制后的状态如下图所示:
进程A的文件描述符表(after dup)
- ------------
- fd0 0 | p0
- ------------
- fd1 1 | p1 -------------> 文件表1 ---------> vnode1
- ------------ /|
- fd2 2 | p2 /
- ------------ /
- fd3 3 | p3 -------------> 文件表2 ---------> vnode2
- ------------ /
- s_fd 4 | p4 ------/
- ------------
- ... ...
- ... ...
- ------------
进程A的文件描述符表(after dup2)
- ------------
- fd0 0 | p0
- ------------
- n_fd 1 | p1 ------------
- ------------ \
- fd2 2 | p2 \
- ------------ _\|
- fd3 3 | p3 -------------> 文件表2 ---------> vnode2
- ------------
- s_fd 4 | p4 ------------->文件表1 ---------> vnode1
- ------------
- ... ...
- ... ...
- ------------
dup(fd)的语意是返回的新的文件描述符与fd共享一个文件表项。就如after dup图中的s_fd和fd1共享文件表1一样。