fcntl函数

目录

  • 一、fcntl函数的作用
  • 二、fcntl 函数的声明
    • 1、参数解析
    • 2、返回值
  • 三、使用fcntl 将文件描述符设置为非阻塞
    • 1、设置非阻塞模式实现
    • 2、代码测试

一、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、参数解析

第一个参数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); //追加文件描述符的状态为非阻塞 
}

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

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

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

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

你可能感兴趣的:(服务器,linux)