常用的5个I/O函数:
read, write, open, lseek以及close
不带缓冲的I/O函数指的函数内是调用内核中的一个系统调用,如
read, write
文件描述符: 内核中用来引用文件,是一个非负整数。文件描述符0表示标准输入
STDIN_FILENO
,描述符1表示标准输出STDOUT_FILENO
,描述符2表示标准出错输出STDERR_FILENO
, 文件描述变化范围:0~OPEN_MAX
,现在系统允许一个进程打开的最大文件数是64
.open函数
头文件#include <fcntl.h> 原型:int open(const char * pathname, int oflag, .../* mode_t mode */); 返回值:若成功则返回文件描述符fd, 失败返回-1. oflag参数说明打开文件的各选项, 由以下一个或多个常量进行或运算构成: O_RDONLY(只读,值为0)、 O_WRONLY(只写,值为1)、 O_RDWR(读写,值为2),这三个变量只能选一个。 O_APPEND(追加到末尾)、O_CREATE(创建文件,需指定第三个参数mode),O_EXCL(和O_CREATE一起用,用以测试文件存不存在,若存在,则出错,若不存在,则创建。使得测试和创建成为一个原子操作) O_TRUNC(如果文件已存在,而且为只读只写打开,则将其长度截短为0) O_NOCTTY(如果pathname是终端设备,则不该将此设备分配作为进程的控制终端) O_NONBLOCK(如果pathname指向的是一个FIFO、一个块特殊文件,一个字特殊文件,则为本次打开操作和以后的I/O操作设置为非阻塞模式) O_SYNC(同步输入输出,使每次write都等待物理I/O操作完成,包括由write引起的文件属性变化所引起的I/O)
OPEN函数返回的文件描述符一定是最小的未用的描述符。
POSIX.1系统中,常量_POSIX_NO_TRUNC决定是要截短过长的文件名和路径名,还是返回一个出错。creat函数 (是creat不是create….)
头文件#include <fcntl.h> 原型:int creat(const char* pathname, mode_t mode); 返回值:若成功则返回只写打开的文件描述符,若出错则返回-1. 等效于: open(pathname, O_WRONLY | O_CREATE | O_TRUNC, mode);
close函数
头文件: #include<unistd.h> 原型: int close(int filedes); 返回值:若成功则返回0, 若出错返回-1. 当进程终止时,内核会自动关闭打开的文件。
lseek函数: 设置文件偏移量
头文件:#include<unistd.h> 原型:off_t lseek(int filedes, off_t offset, int whence); 返回值: 若成功则返回新的偏移量,若失败则返回-1,如果文件描述符引用的是管道、FIFO或网络套接字,则返回-1 对参数offset的解释与whence参数有关: 若whence值为SEEK_SET,则将该文件的偏移量设置为距离开始处offset个字节。 若whence值为SEEK_CUR, 则将该文件的偏移量设置为其当前值加offset,offset可为正或负。
文件偏移量可以大于文件的当前长度,这种情况下, 可能形成文件空洞,文件空洞不要求在磁盘上占用存储区。
read函数
头文件:#include <unistd.h> 原型: ssize_t read(int filedes, void * buf, size_t nbytes); 其中ssize_t为带符号的返回值,size_t为不带符号的返回值。 若成功,返回读到的字节数,若已到文件尾,返回0, 出错则返回-1。
write函数
头文件:#include <unistd.h> 原型: ssize_t write(int filedes, const void * buf, size_t nbytes); 返回值通常与nbytes值相同,若不同则代表出错。
I/O效率: Linux ext2文件系统,块长为4096字节,由st_blksize表示。使用read函数,测试读效率,发现每次读取字节为4096时系统CPU时间最小,因为系统有预读措施。
文件共享
内核使用三种数据结构表示打开的文件:每个进程在进程表中有一个记录项,记录项中包含一个打开文件描述符表,每个描述符占据一项,与文件描述符对应的是:文件描述符标志、指向一个文件表项的指针。
内核为所有打开文件维护一张文件表,每个文件表项包括:①文件状态标志(读、写、添写、同步和非阻塞);②当前文件偏移量;③指向该文件v结点的指针。
每个打开文件(或设备)有一个v结点结构。包含该文件的类型和对此文件进行各种操作的函数的指针,对大多数文件,v结点还包含一个i结点,i结点包含该文件的基本信息(所有者,长度,所在设备,指向文件数据库在磁盘上所在位置的指针)。v结点存在于磁盘上,打开文件时,从磁盘读入内存。lseek函数只改变文件表项中的当前偏移量值,不进行I/O操作。
原子操作: 指由多步组成的操作,这些操作要么全执行,要么全不执行。
dup和dup2函数: 用来复制一个现有的文件描述符。
头文件: #include <unistd.h> 原型: int dup(int filedes); int dup2(int filedes, int filedes2); 返回值:成功则返回新文件描述符,出错返回-1。dup返回的一定是可用文件描述符中的最小值,dup2返回filedes2指定的描述符, 如果filedes2已打开,则先将其关闭。 返回的新描述符与原文件共享一个文件表项。
sync、fsync和fdatasync函数
延迟写技术: 在写文件时,先将内容写至缓冲区,待缓冲区写满或系统需要重用该缓冲区时,再将缓冲区的内容排入输出队列,待其到输出队列队首时,才进行实际的I/O操作。
延迟写降低了磁盘I/O次数,却导致文件内容更新不及时。Unix系统提供三个函数来保证磁盘上实际文件内容与高速缓冲区中的内容一致。头文件:#include <unistd.h> 原型:int fsync(int filedes); int fdatasync(int filedes); 返回值:成功则返回0,出错返回-1. void sync(void);
sync只是将所有修改过的块缓冲区排入写队列,然后返回,并不等待实际写磁盘操作结束。通常update守护进程会周期性调用sync函数, 一般隔30秒。
fsync只对filedes指定的单一文件起作用,并且等待写磁盘操作结束,然后返回,适用于数据库这样的程序。
fdatasync只将文件数据部分同步到磁盘,并不同步文件属性部分。
15. fcntl函数: 可以改变已打开的文件的性质。
头文件:#include <fcntl.h>
原型:int fcntl(int filedes, int cmd, .../* int arg */);
返回值:若成功,则依赖于cmd,若失败则返回-1.
有5种功能:
① 复制一个现有的描述符(cmd = F_DUPFD)。
② 获得/设置 文件描述符标记 (cmd = F_GETFD或cmd = F_SETFD)
③ 获得/设置 文件状态标志 (cmd = F_GETFL 或 cmd = F_SETFL)
④ 获得/设置 异步I/O所有权 (cmd = F_GETOWN 或cmd = F_SETOWN)
⑤ 获得/设置 记录锁(cmd = F_GETLK、F_SETLK或F_SETLKW)
实验表明linux ext2系统并没有实现O_SYNC功能。
16. /dev/fd
较新的系统提供名为/dev/fd的目录,其目录项为0,1,2等的文件,打开文件/dev/fd/n等于复制描述符n。
fd = open(“/dev/fd/0”, mode) 相当于 dup(0), 常常忽略mode。