• 文件控制 fcntl函数具体解释


    摘要:本文主要讨论文件控制fcntl函数的基本应用.dup函数能够拷贝文件描写叙述符,而fcntl函数与dup函数有着异曲同工之妙.而且还有更加强大的功能,能够获取或设置已打开文件的性质,操作文件锁.

    1.fcntl函数

        在《重定向编程 dup和dup2》一文中,介绍了dup和dup2两个函数,函数是提供了复制一个现存的文件描写叙述符的功能,而本文介绍的fcntl函数提供了进一步管理文件描写叙述符的各种手段,用它能够对以打开的描写叙述符运行各种控制操作,比方,能够像dup和dup2一样复制一个文件描写叙述符,还有获取或设置文件描写叙述符标志(仅仅读、仅仅写等等),操纵文件锁.

    头文件:
    #include <unistd.h>
    #include <fcntl.h>
    定义函数:
    int fcntl(int filedes, int cmd);
    int fcntl(int filedes, int cmd, longarg);
    int fcntl(int filedes, int cmd,structflock *lock);
    返回值:若成功返回值依赖与cmd,若出错则返回-1.假设设置属性,正确返回0,错误返回-1;假设是读取属性,正确返回该属性值,错误返回-1.如,下例三个命令有特定的返回值:F_DUPFD、F_GETFD、F_GETFL以及F_GETOWN.
    F_DUPFD:返回新的文件描写叙述符.
    F_GETFD:返回对应的标志.
    F_GETFL以及F_GETOWN:返回一个进程ID或负的进程组ID.
    函数说明:
       函数第一个參数为欲改动属性的文件描写叙述符,第三个整数总是整数或记录锁(记录锁这里不讨论),假设是记录锁,第三个參数则是指向一个结构体指针.
       fcntl()对打开的文件描写叙述符filedes运行各种控制操作,详细是那一操作则由第二个參数cmd决定,表1列出了该參数的全部同意值.依据參数cmd的值,有一些參数还要提供第三个參数,就是文件锁.

    表1 fcntl函数cmd值以及描写叙述
     
    fcntl函数有5种功能:
    1)复制一个现有的文件描写叙述符(cmd=F_DUPFD).
    2)获取或设置文件描写叙述符标志(cmd=F_GETFD或F_SETFD).
    3)获取或设置文件状态标志(cmd=F_GETFL或F_SETFL).
    4)获得或设置异步I/O全部权(cmd=F_GETOWN或F_SETOWN).
    5)获取或设置记录锁(cmd=F_GETLK或F_SETLF).

    2.F_DUPFD

        拷贝文件描写叙述符filefes.新文件描写叙述符最为函数返回值.新文件描写叙述符具有下面特性:
    (1)新描写叙述符是尚未打开的各文件描写叙述符中大于或等于第三个參数值(取正整数)中各值的最小值.
    (2)新描写叙述符与filedes共享同一个文件表项(类似于dup函数);即,新描写叙述符与filedes原始文件同样的文件指针,同样的打开文件(或管道),同样的文件状态标志,同样的文件模式.如图2所看到的,fd=3与fd=1两个文件描写叙述符是指向同一个文件表的.可是,新文件描写叙述符有它自己的一套文件描写叙述符标志,其FD_CLOEXEC文件描写叙述符标志被清除.
    (3)将与新文件描写叙述符关联的close-on-exe标志设置为在各exec(2)系统调用之间保持打开状态.
        当进程打开一个文件时,内核中的数据结构如图1所看到的,也就是在程序拷贝文件描写叙述符之前,内核状态是这样子的。每一个进程默认仅仅能打开1024个文件描写叙述符,当一个进程打开一个文件时,默认会从0開始查找未被使用的描写叙述符,因为0,1,2默认被占用,全部一般从3開始使用。

     
    图1 进程打开一个文件时的内核数据结构状态
    文件表项:每个打开的文件相应着一张文件表,文件表能够共享,当多个文件描写叙述符指向同一个文件表时,文件表中的refcnt字段会相应变化.文件表中包括着文件状态标识:文件的打开模式(R,W,RW,APPEND,NOBLOCK等)、当前文件偏移量、refcnt:被引用数量、v节点指针:指向一个v节点表。
    v结点表:每一个文件相应一个v结点表,不管被多少个进程打开都仅仅有一个,它包含v节点信息(主要是stat结构体中的信息),i节点信息等。
        简单地说,拷贝文件描写叙述符只在当前进程打开的文件表项新增一项(图2:新增了fd=3这一项),两者(fd=1或fd=3)同一时候指向内核中的文件表信息,操作文件描写叙述符随意一个会影响到另外一个.

     
    图2 进程拷贝文件描写叙述符后的内核数据结构状态
    样例1:拷贝文件描写叙述符,并使用复制前后的文件描写叙述符往打开的文件写入信息.
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    void main()
    {
            int fd,newfd;
            char *bufFD="Advanced Programming! write by fd
    ";
            char *bufNewFD="Advanced Programming! write by NewFD
    ";
            fd = open("test.txt",O_RDWR|O_CREAT,0644);
            if(fd==-1)
            {
                    printf("open file error%m
    ");
                    exit(-1);
            }
    
            //開始复制了
            newfd = fcntl(fd,F_DUPFD);
            //使用fd写
            write(fd,bufFD,strlen(bufFD));
            close(fd);
    
            //使用newfd写
            write(newfd,bufNewFD,strlen(bufNewFD));
    
            if(close(newfd)==-1)
            {
                    printf("close error
    ");
            }
            printf("now the content of file :
    ");
            system("cat test.txt");
            exit(0);
    }
    输出:
    :Advanced Programming! write by fd
    :Advanced Programming! write by NewFD
        能够看出,对fd或newfd进行读写操作时对同一个文件操作,并且还能够看到fd关闭后,对newfd没有影响,使用newfd还能够操作打开的文件.
        对于复制一个文件描写叙述符这个问题,fcntl和dup函数是有异曲同工之妙.
    调用:

    dup(filedes);
    等价于:
    fcntl(filedes, F_DUPFD, 0);
    而调用:
    dup2(filedes,filedes2);
    等价于:
    close(filedes2);
    fcntl(filesdes,F_DUPFD,filedes);
    注意:对于后一种情况,dup2并不全然等价于close加上fcntl.差别:
    (1)dup2是原子操作,而close及fcntl则包含两个函数调用.有可能在close和fcntl之间插入运行信号捕获函数,中间这个过程改动了文件描写叙述符.
    (2)dup2和fcntl有某些不同的errno.

    3.F_GETFL

        F_GETFL用于获取文件状态标志和其訪问模式,相应于filedes的文件状态标志作为返回值.F_GETFL支持的标志.如表2.
    表2 文件状态标志说明

    样例2:监測文件当前的读取权限.假设文件具有读取权限,则返回可读信息,假设具有写权限,则返回可写信息.否则返回出错信息.
    #include <sys/types.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
            int accmode,val;
            if(argc!=2)
            {
                    printf("error:
    ");
            }
            if((val=fcntl(atoi(argv[1]),F_GETFL,0))<0)
            {
                    printf("error:%m
    ");
                    exit(-1);
            }
            accmode = val & O_ACCMODE;
            switch(accmode)
            {
                    case O_RDONLY:
                            printf("read only
    ");
                            break;
                    case O_WRONLY:
                            printf("write only
    ");
                            break;
                    case O_RDWR:
                            printf("read write
    ");
                            break;
                    default:
                            printf("unknow access mode
    ");
            }
            exit(0);
    }
    输出:
    :./a.out test.txt
    :read write

    4.综述

        拷贝文件描写叙述符的三种方法已学习的差点儿相同,牢记dup、dup2和fcntl函数,由于,从此一生受用.关于fcntl的使用还有非常多未挖掘,到这里先简单入门,日后再进一步加深学习.
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙筑高台,静下心来,慢慢地沉淀---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  • 相关阅读:
    Ubuntu-18.04 设置开机启动脚本
    香橙派PC Plus开发镜像制作
    Flurl使用Polly添加重试机制
    在Windows上远程调试Ubuntu-Arm上的.Netcore
    NAT
    关于visual studio 2019的参数信息快捷键无效
    spring cloud ip地址注册问题
    spring cloud sleuth集成问题
    spring boot admin 安装问题
    pm2+nuxt 自动重启问题
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4285938.html
Copyright © 2020-2023  润新知