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下编程------文件与IO(三) 文件共享和fcntl函数
- Linux系统编程(3)——文件与IO之fcntl函数
- Linux系统编程(3)——文件与IO之fcntl函数
- 使用java.io.*操作文件的拆分与合并,Application一例!
- 利用System.IO.Compression实现文件压缩和解压缩
- 通用函数库(3) 文件IO操作
- 评《Visual Basic 2005 文件IO与数据存取秘诀》
- 用java的i/o读写文件,举一反三看清io的使用 推荐
- C中的文件与IO操作(一)
- 关于异步文件IO
- linux c 基础学习总结之文件IO
- 使用java.io.*操作文件的拆分与合并,Application一例!
- Java IO 实现文件复制 -Java 学习笔记 (25)
- c++文件io完全手册
- 关于异步文件IO
- 使用java.io.*操作文件的拆分与合并,Application一例!
- 浅析VB.NET文件操作类System.IO
- 解决文件无法上传的问题,错误信息:System.IO.DirectoryNotFoundException: Could not find a part of the path.
- 在System.IO名称空间中文件输入输出的主要类
- System.IO 路径,文件,目录,I/O常见操作汇总(二)