您的位置:首页 > 运维架构 > Linux

8-文件IO-fcntl函数

2016-09-25 21:24 197 查看

回忆文件表

还记得在第7篇文章提到过,阻塞与非阻塞是文件本身的属性吗?再回想一下,第6篇《文件IO-lseek》中提到的一些内核数据结构,每个描述符是某个数组的一个索引,这个数组每个元素保存了一个指向文件表的指针。这个文件表的结构如下。

struct file {
unsigned short f_mode; // 文件权限位
unsigned short f_flags; // 文件状态位
unsigned short f_count; // 引用计数
struct m_inode * f_inode; // 文件存在磁盘上的哪个位置等等其它信息由这个字段来解释
off_t f_pos; // 当前偏移量
};


每次我们通过 open 函数打开一个文件时,open 函数的第二个参数 flags 都会保存到到这 f_flags 成员。

在上一节中,为了让终端文件具备 O_NONBLOCK 属性,我们不得不重新 open 一次。有没有更好的办法,可以让我们不用重新 open,直接修改这个 f_flags 的值?

答曰:有。

fcntl 函数

使用 fcntl,可以让我们直接修改 f_flags 标志。当然了,fcntl 的功能远远不止这些,可是为什么一定要一次性说完呢?不如先来两个小例子看看,fcntl 到底是如何操纵文件表中的成员 f_flags 的。

在给出例子前,先看一下 fcntl 获取和设置已打开文件的方法。

int fcntl(int fd, int cmd = F_GETFL); // 获取文件标志位
int fcntl(int fd, int cmd = F_SETFL, int arg); // 设置文件标志位


注意第二个参数,取不同值的时候,fcntl 有着不同功能。如果失败,fcntl 返回 -1.

例1

下面这段小程序,从参数读取一个数字,这个数字是描述符。然后利用 fcntl 获取这个描述符对应的文件的标志位。

// 文件名:fcntldemo.c
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
if (argc != 2) {
printf("USAGE: fcntldemo [fd]\n");
exit(1);
}

int fd = atoi(argv[1]);

int flags = fcntl(fd, F_GETFL);

if (flags == -1) {
perror("fcntl");
exit(1);
}

if (flags & O_RDONLY) {
printf("O_RDONLY\n");
}
if (flags & O_WRONLY) {
printf("O_WRONLY\n");
}
if (flags & O_RDWR) {
printf("O_RDWR\n");
}
if (flags & O_NONBLOCK) {
printf("O_NONBLOCK\n");
}
if (flags & O_APPEND) {
printf("O_APPEND\n");
}

return 0;

}


编译后执行

$ gcc fcntldemo.c -o fcntldemo
$ ./fcntldemo 5 5>>test // 以追加的形式打开 test,并让描述符 5 指向这个文件。


执行后显示

O_WRONLY
O_APPEND


例2

下面这个例子改写了前面非阻塞读终端的代码,替换了原来使用 open 的方式给终端文件加上O_NONBLOCK标志的方法。

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h> // errno 变量的头文件
#include <stdlib.h>

char MSG_TRY[] =  "try again!\n";

int main() {
char buffer[10];
int len;
int fd;

// 先获取原来的 flags 的值
int flags = fcntl(STDIN_FILENO, F_GETFL);
if (flags == -1) {
perror("fcntl get");
exit(1);
}

flags |= O_NONBLOCK;

// 设置文件表中的 f_flags 成员的值
if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1) {
perror("fcntl set");
exit(1);
}

while(1) {
len = read(STDIN_FILENO, buffer, 10);
if (len < 0) {
if (errno == EAGAIN) {
write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
sleep(1); // 让出 CPU,避免CPU长时间空转
}
else {
perror("read");
exit(1);
}
}
else {
break;
}
}

write(STDOUT_FILENO, buffer, len);
return 0;
}


总结

本文并没有过多的去阐释 fcntl 其它的功能,只讲解了如何使用 fcntl 函数去获取已打开文件的标志位,设置已打开文件的标志位。所以,本篇只要大家熟记 fcntl 的两个命令(fcntl的第2个参数),F_GETFL 和 F_SETFL。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux环境编程 fcntl