• fcntl函数


    一、fcntl函数的作用

    read函数是典型的阻塞模型,当缓冲区里的数据不就绪的时候,会一直阻塞等待。这是正常的,因为文件描述符默认是阻塞IO,而我们可以通过 fcntl 接口函数将文件描述符设置为非阻塞IO

    设置成非阻塞IO以后,read函数会一直检测数据是否就绪,如果就绪就读取,并返回读取到的字符数;如果不就绪,就返回一个错误码

    二、fcntl 函数的声明

    fcntl 函数的作用是操作一个文件的文件描述符,而设置成非阻塞IO只是 fcntl 函数的功能之一。

    函数原型:

    #include
    #include
    int fcntl(int fd, int cmd);
    int fcntl(int fd, int cmd, long arg);
    int fcntl(int fd, int cmd ,struct flock* lock);
    int fcntl(int fd, int cmd, ... /* arg */ );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1、参数解析

    第一个参数fd,指明你要操作哪个文件的文件描述符

    第二个参数cmd,也就是你要对该文件描述符进行何种操作。cmd的取值不同,后面追加的参数也不同。

    cmd的取值命令解析
    F_DUPFD复制一个现有的文件描述符
    F_GETFD 或 F_SETFD获得/设置文件描述符标记
    F_GETFL 或 F_SETFL获得/设置文件状态标记
    F_GETOWN 或 F_SETOWN获得/设置异步IO所有权
    F_GETLK 或 F_SETLK( F_SETLKW )获得/设置记录锁

    第三个参数arg,可有可无,由第二个参数决定,比如F_GETFL时候没有,F_SETFL时候有值

    2、返回值

    执行成功时,不同的cmd可能会对应不同的返回值,没有列举在下面的,比如F_SETFL,可能返回值类型为void。

    成功则返回0,若有错误则返回-1,错误原因存于errno

    三、使用fcntl 将文件描述符设置为非阻塞

    1、设置非阻塞模式实现

    以设置文件描述符为 fd 的文件为例,将文件描述符设置为非阻塞状态,需要用到上述表格的第三个功能,获得/设置文件状态标记。
    基本思路为:第一步,先获取到原有的文件状态;第二步,在原有的基础上追加一种非阻塞状态。

    void setNonBlock(){
        int fl = fcntl(fd,F_GETFL);      //获取文件描述符为fd的文件状态
        if (fl < 0)
        {
            perror("fcntl");
            return;
        }
     
        fcntl(fd, F_SETFL, fl | O_NONBLOCK); //追加文件描述符的状态为非阻塞 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2、代码测试

    函数准备好以后,在调用read函数之前先将 第fd文件描述符设置成非阻塞,然后再调用read函数。

    int main(){
        setNonBlock();     //设置为非阻塞模式 
        
        while (1)
        {
            char buffer[1024];
            ssize_t s = read(fd,buffer,sizeof(buffer)-1);        //非阻塞读取
            if (s > 0)
            {
                buffer[s] = 0;
                write(1,buffer,s);        //将读取到的内容打印到屏幕上
                printf("read success, res: %d, content: %s\n",s, buffer);
            }
            else{
                if(errno == EAGAIN || errno == EWOULDBLOCK){
                    printf("read failed, res: %d , errno: %d\n", s , errno);
                }
            }
     
            sleep(2);    
        }
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    测试结果如下,非阻塞模式下,read函数如果发现数据尚未就绪,系统是以出错的形式返回的,很显然数据未就绪不算错误,那么要如何区分真正的错误 和 数据未就绪时的出错呢?

    答案是errno,errno是错误码,正常情况下是0。当数据没有就绪的时候,errno的值是11,即EAGAIN 或者 EWOULDBLOCK,我们可以以此判断是否真的出错了。如果你在成功时打印errno,你会发现errno仍然是11,因为read读取到数据的时候,不会设置errno。

    read函数里有对EAGAIN的解释,EAGAIN会出现在当某个文件描述符被设置成非阻塞的时候。

  • 相关阅读:
    mongodb备份还原指南
    阻塞队列BlockingQueue
    Redis设计与实现(八)| 事务
    MindFusion Spreadsheet for Java 1.0.1
    x.ai还是OpenAI?埃隆·马斯克的AI帝国【2】
    文件查询匹配神器 【glob.js】 实用教程
    别再纠结线程池池大小、线程数量了,哪有什么固定公式 | 京东云技术团队
    线粒体 ClpP 介导的蛋白水解作用可选择性诱导癌细胞死亡
    送福利,低代码平台助力企业数字化转型
    【深度学习环境配置】windows出现出现‘git‘ 不是内部或外部命令,也不是可运行的程序
  • 原文地址:https://blog.csdn.net/qq_40685457/article/details/133579951