打开文件:open()
open既能打开一个已存在的文件,也可以创造一个新文件。当调用open()创建新文件时,位掩码参数mode指定了文件的访问权限。如果open()未指定O_CREAT标志,则可以省略mode参数。
#include <sys/stat.h>
#include <fcntl.h>
int open(const char* pathname, int flags, ... /* mode_t mode*/);
// return file descriptor on success, or -1 on error
/*
*pathname :路径名,如果是符号链接,会对其进行解引用
*flags :位掩码,用于指定 文件的访问模式
*return :成功返回文件描述符,失败则返回-1
*/
文件访问模式:
访问模式 | 描述 |
---|---|
O_RDONLY | 以只读方式打开文件 |
O_WRONLY | 以只写方式打开文件 |
O_RDWR | 以读写方式打开文件 |
flag参数除了使用文件访问标志外,还用了其他操作标志。其他open系统调用的flags参数值:
标志 | 用途 |
---|---|
O_CLOEXEC | 设置close-no-exec标志 |
O_CREAT | 如果文件不存在,则创建文件 |
O_DIRECT | 无缓冲的输入/输出 |
O_DIRECTORY | 如果pathname不是目录,则失败 |
O_EXCL | 结合O_CREAT使用,专门用于创建文件 |
O_LARGEFILE | 在32位系统中使用此标志 打开大文件 |
O_NOATIME | 调用read()时,不修改文件最近访问时间 |
O_NOCTTY | 不要让pathname成为控制终端 |
O_NOFOLLOW | 对符号链接不予解引用 |
O_TRUNC | 截断已有文件,使其长度为零 |
O_APPEND | 总在文件尾部追加数据 |
O_ASYNC | 当I/O操作可行时,产生信号通知进程 |
O_DSYNC | 提供同步的I/O数据完整性 |
O_NONBLOCK | 以非阻塞方式打开 |
O_SYNC | 以同步方式写入文件 |
通过fcntl()的F_GETFL操作可以获取到文件的访问模式。
open文件出错
在打开文件出现错误时,open()函数调用将会返回-1,并通过设置错误号errno来标识错误原因。可能的错误号有:
- EACCES:文件权限不允许调用进程以flags参数指定的方式打开文件,可能是访问权限不够或者文件不存在等
- EISDIR:所指定的文件为目录,而调用者企图对目录写入
- EMFILE:进程所打开的文件描述符数量达到了进程资源限制的上限
- ENFILE:文件打开数量达到了系统允许的上限
- ENOENT:文件不存在且未指定O_CREAT标志,或者pathname所指定的路径有问题
- EROFS:文件只读,无法以只写或者读写的方式打开
- ETXTBSY:文件为可执行程序,且正在执行
读取文件内容:read()
read()系统调用从文件描述符fd所指代的打开文件中读取数据
#include<unistd.h>
ssize_t read(int fd, void *buffer, size_t count);
count参数指定最多能读取的字节数,buffer参数提供用来存放输入数据的内存缓冲区地址,且缓冲区至少应有count个字节。
如果read()调用成功,将会返回实际读取的字节数,如果遇到文件结束(EOF)则返回0,如果出现错误则返回-1。
数据写入文件:write()
write()系统调用将数据写入一个已打开的文件中。
#include<unistd.h>
ssize_t write(int fd, void* buffer, size_t count);
//return number of bytes written or -1 on error
buffer参数为要写入文件中数据的内存地址,count参数为想要从buffer写入文件的数据字节数,fd参数为文件描述符,指代数据要写入的文件。
对磁盘文件执行I/O操作时,write()调用成功并不能保证数据已写入磁盘。这是因为减少磁盘活动量和加快write()系统调用,内核会缓存磁盘的I/O操作。
关闭操作:close()
close()系统调用会关闭一个打开的文件描述符,并将其释放回调用进程,供进程继续使用。当一个进程终止时,会自动关闭已打开的所有文件描述符。
#include<unistd.h>
int close(int fd);
//return 0 on success or -1 on error
改变文件偏移量:lseek()
对于每个打开的文件,系统内核会记录其文件偏移量。文件偏移量是指执行下一个read()或write()操作的文件起始位置,会以相对于文件头部起始点的文件当前位置来表示。文件第一个字节的偏移量为0。
文件打开时,会将文件偏移量设置为指向文件开始,以后每次read()和write()调用将按顺序递进,对文件进行操作
针对文件描述符fd参数所指代的已打开文件,lseek() 系统调用按照offset和whence参数值调整该文件的偏移量。
#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);
//return new file offset if successful or -1 on error
offset参数指定了一个以字节为单位的数值。whence参数则表明应参照哪个基点来解释offset参数,主要有以下几个:
SEEK_SET:将文件偏移量设置为从文件头部起始点开始的offset个字节
SEEK_CUR:相对于当前文件偏移量,将文件偏移量调整offset个字节
SEEK_END:将文件偏移量设置为起始于文件尾部的offset个字节
lseek()并不适用于所有类型的文件,不允许将lseek()应用于管道、FIFO、socket或者终端。
文件空洞
从文件结尾后到新写入数据间的这段空间被称为文件空洞。从编程角度看,文件空洞中是存在字节的,读取空洞将返回0填充的缓冲区。
然而,文件空洞不占用任何磁盘空间。直到后续某个时点,在文件空洞中写入了数据,文件系统才会为之分配磁盘块。其优势在于与实际分配的空字节分配磁盘块相比,稀疏填充的文件会占用较少的磁盘空间。
通用I/O模型以外的操作:ioctl()
ioctl()系统调用为执行文件和设备操作提供了一种多用途机制。
#include<sys/ioctl.h>
int ioctl(int fd, int request, ... /*argp*/);
//Value returned on success depends on request, or -1 or error
fd参数为某个设备或已打开的文件描述符,request参数指定了将在fd上执行的控制操作。