高级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;
}

 

posted on 2021-09-15 15:06  freden  阅读(179)  评论(0编辑  收藏  举报