认识 fcntl 接口函数(文件非阻塞设置)

  • Post author:
  • Post category:其他



目录


一、fcntl函数的作用


二、fcntl 函数的声明


1、参数解析


2、返回值解析


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


1、设置非阻塞模式实现


2、代码测试


一、fcntl函数的作用

read函数是典型的阻塞模型,当缓冲区里的数据不就绪的时候,会一直阻塞等待。这是正常的,因为

文件描述符默认是阻塞IO

,而我们可以通过 fcntl 接口函数将文件描述符设置为非阻塞IO。

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

二、fcntl 函数的声明

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

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 ) 获得/设置记录锁

2、返回值解析

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

执行出错时,也会有不同的返回值,此时可能就与cmd无关了。

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

1、设置非阻塞模式实现

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

void setNonBlock(){
    int fl = fcntl(0,F_GETFL);      //获取文件描述符为0的文件状态
    if (fl < 0)
    {
        perror("fcntl");
        return;
    }

    fcntl(0, F_SETFL, fl | O_NONBLOCK); //追加文件描述符的状态为非阻塞 
}

2、代码测试

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

int main(){
    setNonBlock();     //设置为非阻塞模式

    while (1)
    {
        char buffer[1024];
        ssize_t s = read(0,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;
}

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

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

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



版权声明:本文为challenglistic原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。