文件描述符复制和重定向
文档转自:文件描述符复制与重定向
一、dup
dup函数的作用是复制文件描述符,这样就有多个文件描述符可以指向同一个文件了。函数原型如下:
1 2
| #include <unistd.h> int dup(int oldfd);
|
- 参数:
oldfd是要被复制的文件描述符
- 返回值:函数调用成功返回被复制出的文件描述符,调用失败返回
-1
下图展示了,dup()函数具体行为, 这样不过使用 fd1还是使用fd2都可以对磁盘文件A进行操作了

被复制出的新文件描述符是独立于旧的文件描述符的,二者没有连带关系。也就是说当旧的文件描述符被关闭了,复制出的新文件描述符还是可以继续使用的。
示例:
通过dup()函数进行文件描述符复制, 并验证了复制之后两个新、旧文件描述符是独立的,二者没有连带关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h>
int main() { int fd = open("./mytest.txt", O_RDWR|O_CREAT, 0664); if(fd == -1) { perror("open"); exit(0); } printf("fd: %d\n", fd);
const char* pt = "你好, 世界......"; write(fd, pt, strlen(pt));
int newfd = dup(fd); printf("newfd: %d\n", newfd);
close(fd);
const char* ppt = "新文件描述符,也可以对文件 ./mytest.txt 进行读写操作"; write(newfd, ppt, strlen(ppt)); close(newfd);
return 0; }
|
二、dup2
dup2() 函数是 dup() 函数的加强版,基于dup2() 既可以进行文件描述符的复制, 也可以进行文件描述符的重定向。文件描述符重定向就是改变已经分配的文件描述符关联的磁盘文件
1 2 3 4 5 6 7 8 9
| #include <unistd.h>
int dup2(int oldfd, int newfd);
|
- 参数:
oldfd和``newfd` 都是文件描述符
- 返回值: 函数调用成功返回新的文件描述符, 调用失败返回 -1
关于这个函数的两个参数虽然都是文件描述符,但是在使用过程中又对应了不同的场景,具体如下:
假设参数oldfd对应磁盘文件 a.txt, newfd对应磁盘文件b.txt。在这种情况下调用dup2函数, 是给newfd做了重定向,newfd 和文件 b.txt断开关联, 相当于关闭了这个文件, 同时 newfd 指向了磁盘上的a.txt文件,最终oldfd和 newfd 都指向了磁盘文件 a.txt

假设参数 oldfd 对应磁盘文件 a.txt, newfd不对应任何的磁盘文件(newfd 必须是一个大于等于0的整数)。在这种情况下调用dup2函数, 在这种情况下会进行文件描述符的复制,newfd 指向了磁盘上的a.txt文件,最终oldfd和 newfd 都指向了磁盘文件 a.txt

假设参数 oldfd 和newfd两个文件描述符对应的是同一个磁盘文件 a.txt 在这种情况下调用dup2函数, 相当于啥也没发生, 不会有任何改变

示例:
给dup2() 的第二个参数指定一个空闲的没被占用的文件描述符就可以进行文件描述符的复制了, 示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h>
int main() { int fd = open("./111.txt", O_RDWR|O_CREAT, 0664); if(fd == -1) { perror("open"); exit(0); } printf("fd: %d\n", fd);
const char* pt = "你好, 世界......"; write(fd, pt, strlen(pt));
int fd1 = 1023;
dup2(fd, fd1);
close(fd);
const char* ppt = "fd1描述符,也可以对文件 ./111.txt 进行读写操作"; write(fd1, ppt, strlen(ppt)); close(fd1);
return 0; }
|
将两个有效的文件描述符分别传递给 dup2() 函数,就可以实现文件描述符的重定向了。将第二个参数的文件描述符重定向到参数1文件描述符指向的文件上。 示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h>
int main() { int fd = open("./111.txt", O_RDWR|O_CREAT, 0664); if(fd == -1) { perror("open"); exit(0); } printf("fd: %d\n", fd);
const char* pt = "你好, 世界......"; write(fd, pt, strlen(pt));
int fd1 = open("./222.txt", O_RDWR|O_CREAT, 0664); if(fd1 == -1) { perror("open1"); exit(0); }
dup2(fd, fd1);
close(fd);
const char* ppt = "fd1描述符,也可以对文件 ./111.txt 进行读写操作"; write(fd1, ppt, strlen(ppt)); close(fd1);
return 0; }
|
三、fcntl
fcntl() 是一个变参函数, 并且是多功能函数,在这里只介绍如何通过这个函数实现文件描述符的复制和获取/设置已打开的文件属性。该函数的函数原型如下:
1 2 3 4
| #include <unistd.h> #include <fcntl.h>
int fcntl(int fd, int cmd, ... );
|
参数:
fd: 要操作的文件描述符
cmd: 通过该参数控制函数要实现什么功能
返回值:
fcntl() 函数的 cmd 可使用的参数列表:

文件的状态标志指的是在使用open()函数打开文件的时候指定的flags 属性, 也就是第二个参数
1
| int open(const char *pathname, int flags);
|
下表中列出了一些常用的文件状态标志:

(1)复制文件描述符
使用 fcntl() 函数进行文件描述符复制, 第二个参数 cmd需要指定为 F_DUPFD(这是个变参函数其他参数不需要指定)。
1
| int newfd = fcntl(fd, F_DUPFD);
|
使用 fcntl() 复制文件描述符, 函数返回值为新分配的文件描述符,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h>
int main() { int fd = open("./mytest.txt", O_RDWR|O_CREAT, 0664); if(fd == -1) { perror("open"); exit(0); } printf("fd: %d\n", fd);
const char* pt = "你好, 世界......"; write(fd, pt, strlen(pt));
int newfd = fcntl(fd, F_DUPFD); printf("newfd: %d\n", newfd);
close(fd);
const char* ppt = "新的文件描述符newfd,也可以操作 ./mytest.txt 文件"; write(newfd, ppt, strlen(ppt)); close(newfd);
return 0; }
|
(2)设置文件状态标志
通过 open()函数打开文件之后, 文件的flag属性就已经被确定下来了,如果想要在打开状态下修改这些属性,可以使用 fcntl()函数实现, 但是有一点需要注意, 不是所有的 flag 属性都能被动态修改, 只能修改如下状态标志: O_APPEND ,O_NONBLOCK, O_SYNC, O_ASYNC, O_RSYNC等。
得到已打开的文件的状态标志,需要将 cmd 设置为 F_GETFL,得到的信息在函数的返回值中
1
| int flag = fcntl(fd, F_GETFL);
|
设置已打开的文件的状态标志,需要将 cmd 设置为 F_SETFL,新的flag需要通过第三个参数传递给 fcntl() 函数
1 2 3 4 5 6
| int flag = fcntl(fd, F_GETFL);
flag = flag | O_APPEND;
fcntl(fd, F_SETFL, flag);
|
示例:
通过fcntl()函数获取/设置已打开的文件属性
往当前文件中写数据, 打开一个新文件, 文件的写指针在文件头部,数据默认也是写到文件开头,如果不想将数据写到文件头部, 可以给文件追加一个O_APPEND属性。实例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h>
int main() { int fd = open("./111.txt", O_RDWR); if(fd == -1) { perror("open"); exit(0); } printf("fd: %d\n", fd);
int flag = fcntl(fd, F_GETFL); flag = flag | O_APPEND; fcntl(fd, F_SETFL, flag);
const char* ppp = "使用文件描述符fd,添加的数据写到文件尾部进行追加"; write(fd, ppp, strlen(ppp)); close(fd);
return 0; }
|