高级I/O函数(一)dup/dup2/fcntl
一、dup与dup2:
#include<unistd.h>
int dup(int file_descriptor);
复制文件描述符(file_descriptor)到另一个对象(返回值,在文件描述符池中去找新的)使得两个文件描述符是指向同一个文件的,而且可以通过这两个文件描述符对文件进行读写。
#include<fcntl.h> #define FILE_NAME "./file.txt" int main(){ int fd1=0; int fd2=0; fd1=open(FILE_NAME,O_RDWR|O_TRUNC);//open a file and get it's descriptor if(fd1==-1) perror("open file error"); fd2=dup(fd1); printf("fd1=%d,f2=%d\n",fd1,fd2); write(fd1,"hello\n",6); write(fd2,"world\n",6); }
dup2:
#include<unistd.h>
int dup2(int oldfd,int newfd);
功能同dup,不过可以指定新文件描述符,若该新文件描述符被占用,则将其关闭后再使用;
fd2=5; fd2=dup2(fd1,fd2); printf("fd1=%d,f2=%d\n",fd1,fd2);
1、实现文件共享:使用dup和dup2复制方式实现文件共享,不管复制出多少个文件描述符,他们永远只有一个文件表,所以使用所有描述符去操作文件时,最后使用的都是通过同一个文件位移量,不管谁操作文件位移量都会更新,因此不会出现覆盖;
2、实现重定位
(1)什么是重定位:
file_des->A,file_des_output->A <<<====重定位====>>> file_des->B,file_des_output->B
文件描述符所指向的文件改变了,使得数据输出的目标文件也随之改变。
(2)dup和dup2实现printf重定位:
要求:printf_output->标准输出文件(显示器)====重定向==== printf_output->file.txt
思路:printf底层调用的write函数,文件描述符是指向标准输出文件的,现在将其重定位使之指向file.txt即可;
步骤:
open file.txt->file_des=3;//获取需要输出的文件对应的文件描述符 close(stand_file_output_des);//关闭标准输出文件,一般标准输出文件的文件描述符是1 stand_file_output_des=dup(file_des);//重定位
代码:
#include<fcntl.h> #define FILE_NAME "./file.txt" void relocation(){ int file_output_des=1; int file_log=open(FILE_NAME,O_RDWR|O_TRUNC); if(file_log==-1) perror("open file error"); close(file_output_des);//关闭标准输出文件描述符,此时无法输出,因为找不到输出文件 dup(file_log);//只需要这样调用,因为dup会从文件描述符池中找到一个最小的,此时最小的就是1,因此将file_log文件描述符复制到1上,那么接下来printf对1进行写操作自然就写到了自定义文件上
//使用dup2更简单,dup2(file_log,file_output_des),可取缔上面两行,因为dup2包含了关闭目标文件描述符的操作 printf("hello world\n"); }
使用场景:函数中的文件描述符写死,无法修改为新的文件描述符,可通过这种方式对文件描述符进行重定位;
应用:CGI服务器的原理就是重定位了标准输出文件描述符
... struct sockaddr_in caddr; socklen_t len=sizeof(caddr); int connfd=accept(sock,(struct sockaddr*)&caddr,&len); if(connfd>=0){ close(STDOUT_FILENO); dup(connfd);//STDOUT_FILENO->connfd(client socket) printf("aaaa\n"); close(connfd); } ...
二、fcntl(file control)函数:对文件描述符的操作
#include<fcntl.h> int fcntl(int fd,int cmd,...)
-》参数说明:fd:指向打开的文件 cmd:控制命令,通过指定不同的宏来修改fd所指向文件的性质。
1.F_DUPFD
作用:复制描述符,用于模拟dup和dup2;
返回值:返回复制后的新文件描述;
2.F_GETFL,F_SETFL
作用:获取、设置文件状态标志
返回值:返回文件的状态标志
-》场景:补设,增加文件状态标志,控制文件描述符常用的属性和行为;
实例:
调用两次open得到了指向同一个文件的两个不同的文件描述符,因此通过文件描述符对文件进行读写时,其指针是不同的,故而会对源文件造成覆盖写的情况;
#include<fcntl.h> #include<stdio.h> #include<unistd.h> #define FILE_NAME "./file.txt" int open_fun1() { int fd=open(FILE_NAME,O_RDWR|O_TRUNC); if(fd==-1){ printf("open file error"); return 0; } return fd; } int open_fun2() { int fd=open(FILE_NAME,O_RDWR|O_TRUNC); if(fd==-1){ printf("open file error"); return 0; } return fd; } int main(){ int fd1=open_fun1(); int fd2=open_fun2();while(1){ write(fd1,"Hello\n",6); sleep(1); write(fd2,"World\n",6); } return 0; }
可以利用fcntl修改文件描述符的状态标志(O_TRUNC|O_APPEND)来实现文件在两个函数中的共享:
int main(){ int fd1=open_fun1(); int fd2=open_fun2(); //直接使用F_SETFL,会直接使用新的标志,去覆盖掉旧的标志 int flag=O_WRONLY|O_TRUNC|O_APPEND; fcntl(fd1,F_SETFL,flag); //保留原有标志,并在原有标志的基础上进行追加新标志 flag=fcntl(fd2,F_GETFL,0);//获取原有标志 flag=flag|O_TRUNC|O_APPEND;//追加新标志 fcntl(fd2,F_SETFL,flag);//设置新标志 while(1){ write(fd1,"Hello\n",6); sleep(1); write(fd2,"World\n",6); } return 0; }