系统编程-文件IO-dup和dup2系统调用
在linux下,一切皆文件。
文件描述符用于操作文件。
从shell中运行一个进程,默认会有3个文件描述符存在(0、1、2);)0表示标准输入,1表示标准输出,2表示标准错误。
一个进程当前有哪些打开的文件描述符可以通过/proc/进程ID/fd目录查看。
1、 dup函数
#include <unistd.h>
int dup(int oldfd);
功能:复制一个文件描述符
返回值:成功则返回一个新的文件描述符,失败则返回-1。
当复制成功时,返回值是当前进程可用的最小的文件描述符,返回的新文件描述符和参数oldfd指向同一个文件,
若有错误则返回-1,错误代码存于errno中。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("a.txt", O_RDWR | O_CREAT);
if(fd == -1)
{
perror("open");
exit(1);
}
printf("file open fd = %d\n", fd);
// 找到进程文件描述表中第一个可用的文件描述符A
// 将参数指定的文件描述符B复制给A,并返回A
int fd2 = dup(fd);
if(fd2 == -1)
{
perror("dup");
exit(1);
}
printf("dup fd = %d\n", fd2);
char* buf = "hello";
char* buf1 = " world\n";
write(fd, buf, strlen(buf));
write(fd2, buf1, strlen(buf1));
close(fd);
return 0;
}
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# ./ab file open fd = 3 dup fd = 4 root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# cat a.txt hello world root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup#
2. dup2函数
#include <unistd.h>
int dup2(int oldfd, int newfd);
功能:复制一个文件描述符,且指定文件描述符newfd为oldfd的复制版本。
返回值:成功返回newfd,失败返回-1。
dup2函数成功返回时,目标描述符(函数第二个参数,newfd)将变成源描述符(函数第一个参数,oldfd)的复制品,
即,两个文件描述符现在都指向同一个文件,并且是源描述符指向的文件。
若有错误则返回-1,错误代码存于errno中。
dup2详解:
①、如果newfd已经打开,则先将其关闭,再指定文件描述符newfd为oldfd的复制版本。
②、如果newfd等于oldfd,则dup2直接返回newfd, 而不关闭它。
//测试dup2函数 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd = open("b.txt", O_RDWR | O_CREAT); if(fd == -1) { perror("open"); exit(1); } int fd1 = open("a.txt", O_RDWR); if(fd1 == -1) { perror("open"); exit(1); } printf("fd = %d\n", fd); printf("fd1 = %d\n", fd1); int curfd = dup2(fd, fd1); //让fd1和fd同时指向b.txt if(curfd == -1) { perror("dup2"); exit(1); } printf("current fd = %d\n", curfd); char* buf = "hello\n"; char* buf1 = "world!\n"; write(fd, buf, strlen(buf)); write(fd1, buf1 , strlen(buf1)); close(fd); close(fd1); return 0; }
编译运行:
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# gcc dup2.c -o ab root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# ./ab fd = 3 fd1 = 4 current fd = 4 root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# cat b.txt hello world! root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# cat a.txt hello world root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup#
实验前后,a.txt的内容未发生改变,同时根据实验后b.txt的内容,证实了dup2函数功能的有效性。
注意:
通过dup和dup2创建的文件描述符,不继承原文件描述符的close-on-exec和non-blocking属性。
细节补充:
int dup(int oldfd);
dup函数:函数执行成功时,新得到的文件描述符和oldfd,共享文件偏移量和文件状态。
共享偏移量测试代码:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> // off_t lseek(int fd, off_t offset, int whence);第二个参数是偏移量,第三个参数是起始地址,要注意区分 int main(int argc, char const *argv[]) { int fd = open("b.txt", O_RDONLY); //1. 先复制fd,得到copyfd int copyFd = dup(fd); //2. 然后对fd、copyfd中的一个文件描述符进行操作,观察另一个文件描述符的变化 //置fd对应的当前文件数据索引偏移量到文件尾 unsigned long offset = lseek(fd, 0, SEEK_END); // 打印偏移量 printf("fd = %d , 距离文件头的偏移量:%ld\n", fd, offset); //打印copyFd的文件数据索引偏移量 printf("copyFd = %d , 距离文件头的偏移量:%ld\n", copyFd, lseek(copyFd, 0, SEEK_CUR)); return 0; }
编译运行:
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# gcc dup_2.c -o ab root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# ./ab fd = 3 , 距离文件头的偏移量:14 copyFd = 4 , 距离文件头的偏移量:14 root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup#
现象和结论:使用了dup,操纵fd的文件数据索引偏移,导致copyfd的偏移量也跟着移动到了文件末尾。
int dup2(int oldfd, int newfd);
dup2函数:函数执行成功时,新得到的文件描述符newfd和oldfd,共享文件偏移量和文件状态。
共享偏移量测试代码:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char const *argv[]) { int fd = open("b.txt", O_RDONLY); int newfd; //1. 获取有效的newfd newfd = dup2(fd, 14); //2. 然后对fd、newfd中的一个文件描述符进行操作,观察另一个文件描述符的变化 //置fd对应的文件数据索引偏移到文件尾 unsigned long offset = lseek(fd, 0, SEEK_END); // 打印偏移量 printf("fd = %d , 距离文件头的偏移量:%ld\n", fd, offset); //打印newfd的文件数据索引偏移 printf("newfd = %d , 距离文件头的偏移量:%ld\n", newfd, lseek(newfd, 0, SEEK_CUR)); return 0; }
编译运行:
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# gcc dup_2.c -o ab root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# ./ab fd = 3 , 距离文件头的偏移量:14 newfd = 14 , 距离文件头的偏移量:14 root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup#
现象和结论:使用了dup2,操纵fd的文件数据索引偏移,导致newfd的偏移量也跟着移动到了文件末尾。
.