要看见树木,也要看见森林。
文件IO,即与操作文件相关的输入输出函数。
POSIX标准定义了,linux文件IO函数指不带缓冲的IO函数,这些函数中的read和write将直接调用内核中的相应函数。
不带缓冲指数据直接由用户层到达内核层。
程序要操作文件,必然需要在程序中用一种虚拟的方式来表示物理存在的文件。这种虚拟的方式就是文件描述符。
文件描述符可以理解为一个索引,通过这个索引能够获取一个文件的所有信息,例如偏移量,文件类型,访问权限等。
打开一个文件,从上层到内核将涉及到3个结构体链表,分别是:文件描述符表、打开文件表和i-node表。它们之间的关系如下:
首先,文件是存储在文件系统中的,文件的属性、权限等信息,是文件系统在维护。一个文件拷贝到文件系统,文件系统将读取文件头部信息,并将文件的属性、权限等信息存储在i-node表中,然后写入磁盘设备中。
同时内核在运行时会维护一个打开文件表,当open文件时,内核将在打开文件表中创建一个结构体成员,存放文件信息,例如,当前文件偏移量、状态标识、文件放文件权限、文件属性等,而信息是从i-node表中读取到的。
open一个文件,将进入内核调用open相对应的系统函数,该系统函数在内核层为这个进程(调用open的进程)创建一个文件描述符表。文件描述符表包含有文件描述符和指向打开文件表的指针。
文件描述符:即在open时,内核给用户分配的一个非负整数。
打开文件表指针:指向内核的打开文件表中与该文件相关的结构体成员的地址。
可以理解文件描述符和打开文件表指针是绑定在一起的。应用层通过文件描述符和内核沟通,内核内部通过对应的打开文件表指针找到文件信息,并返回给应用层。
之后用户就可以通过该文件描述符,调用read/write,就找到打开文件表中与本文件相关的信息,例如文件内容等,然后内核调用相对应的系统调用函数,在read时,向上层返回查询到的信息,在write时,向文件系统磁盘上写入数据。
open和openat
函数声明:
#include
int open(const char *path, in oflag, .../* mode_t mode */)
int openat(int fd, const char *path, in oflag, .../* mode_t mode */)
参数说明:
path: 待打开或创建文件的名字(带路径)
oflag:
下面这几个常量必须指定一个且只能指定一个:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR: 读写打开
O_EXEC: 只执行打开,无法读写
下面常量是可选的
O_APPEND: 每次写文件追加到文件尾端
O_CREAT: 若此文件不存在则创建它。若使用此选项,open函数需同时指定第3个参数,以指定访问权限位
O_EXCL: 如果同时指定了O_CREAT,而文件已经存在,则出错。可用此参数判断一个文件是否存在,如果不存在,则创建。
O_NONBLOCK: 设置后续对此此文件的io操作为阻塞方式。
O_SYNC: 使每次write等待物理IO操作完成。
mode:指定创建文件的访问权限,例0755 ,==》0:十进制;7:111,用户权限;5:101,读+可执行,组权限;5:其他用户权限
返回值:
成功返回非负整型文件描述符,失败返回-1并设置errno。
例1:
int ret = 0;
//读写一个文件,如果文件不存在则创建
ret = open("/path/file.txt", O_RDWR | O_CREAT, 0755);
if(ret < 0)
{
perror("open");
exit(-1);
}
//判断一个文件是否存在
ret = open("/path/file.txt", O_RDWR | O_CREAT | O_EXCL, 0755);
if(ret < 0)
{
printf("file exit!\n");
}
当一个进程终止时,内核自动关闭它所有的打开文件。
函数声明:
include <unistd.h>
int close(int fd);
设置当前文件的偏移量。
函数声明:
#include
off_t lseek(int fd, off_t offset, int whence);
参数说明:
fd: 文件描述符
offset: 字节为单位,例如7,指偏移量为7。具体如果偏移,需要看whence。
whence: 指定偏移的起点
SEEK_SET: 文件的开始处
SEEK_CUR: 当前文件的偏移值
SEEK_END: 文件的末尾处
返回值:
success: 返回新的文件偏移量
fail:-1,在errno中存放错误代码
例1.
//据文件开始处偏移10个字节
off_t offset;
offset = lseek(fd, 10, SEEK_SET);
函数声明:
#include
ssize_t read(int fd, void *buf, size_t nbytes)
返回值:
success: 非负数。返回读取到的字节数,如果到达文件尾端,则返回0。
fail:-1
例1:
ssize_t nBytes;
nBytes = read(fd, buf, sizeof(buf));
if(nBytes < 0)
perror("read");
函数声明:
#include
ssize_t write(int fd, const void *buf, size_t nbytes);
返回值:
success: 返回写入的字节数,即nbytes
fail: -1
设置/获取已经打开文件的属性。
函数声明:
#include
int fcntl(int fd, int cmd, .../* int arg */);
例1、修改文件描述符标志位
void setFl(int fd, int flags)
{
int val;
if((val = fcntl(fd, F_GETFL, 0) < 0))
printf("fcntl F_GETFL error\n");
val |= flags;
if(fcntl(fd, F_SETFL, val) < 0)
printf("fcntl F_SETFL error\n");
}