2016-01-29
文件IO
大多数文件io只需要用到5个函数 open read write lseek close。不带缓存的io:每个read和write都调用内核中的一个系统调用。这些函数不是ansi c的组成部分但是是POSIX和xpg3的组成部分。
文件描述符
对于内核而言,所有打开文件都由文件描述符引用。为一个非负整数。按照惯例,unix shell使用文件描述符0与进程的标准输入结合,1与标准输出相结合,文件描述2与标准出错输出相结合。
在POSIX应用中幻数0 1 2 分别被STDIN_FILENO STDOUT_FILENO STDERR_FILENO来代替
open函数
int open(const char *pathname, int oflag, ...)
当且仅当创建新文件时才使用第三个函数。oflag 可以使用 O_RDONLY O_WRONLY O_RDWR(读写) O_APPEND O_CREATE(若文件不存在则创建,需同时说明第三个参数) O_EXCL(如果同时指定了O_CREATE而且文件已经存在,则出错)
O_TRUNC(如果此文件存在而且为只读或者只写成功打开,则将其长度截短为0) O_NOCITY(如果pathname指的是终端设备,则不将此设备分配作为此进程的控制终端) O_NONBLOCK(如果pathname指的是一个FIFO、一个块特殊文件或者一个字符特殊文件,则此选项为此文件的本次打开操作和后续的io操作设置非阻塞方式) O_SYNC使每次write都等到物理io操作完成。
create函数
也可以使用create函数创建一个新文件
int create(const char *pathname, mode_t mode); 此函数等价为 open(pathname,O_WRONLY|O_CREATE|O_TRUNC,mode)
close函数
可以使用close函数关闭一个打开的文件,关闭一个文件时也是放该进程加载该文件上的所有记录锁
当一个进程终止时,它所有的打开文件都由内核自动关闭。
lseek函数
每个打开文件都有一个与其相关联的当前文件位移量,它是一个非负整数,用以度量从文件开始处计算的字节数。通常读写操作都从当前文件位移量处开始,并使用位移量增加所读或者所写的字节数。按系统默认,当打开一个文件时,除非指定0_APPEND否则改位移量为0
off_t lseek(int filedesm off_t offset, int whence);
offset的解释与参数whence的值有关
若whence是SEEK_SET则指定位置,SEEK_CUR指定相对位置,SEEK_END文件长度加上offset
如果文件描述符引用的是一个管道或者FIFO则seek返回-1并将errno设置为EPIPE
通常,文件当前位移量是非负整数,但是某些设备可能允许负的位移量,所以在比较lseek的返回值时不要测试它是否小于零而要测试其是否等于-1。seek 的大小可以超过文件的长度,这样下一次写入操作会延长该文件
read函数
用read函数从打开文件中读取数据
ssize_t read(int filedes, void *buff, size_t nbytes) 返回读到的字节数,若已到文件尾为0,出错返回-1
读位移量增加实际读的字节数
有许多情况实际督导的字节数少于要求读字节数
- 读普通文件时,在读到要求字节数之前已经到达了文件尾端
- 从网络读时,网络中的缓冲机构可能造成返回值小于所要求的读字节数
- 某些面向记录的设备,如磁带,一次最多返回一个记录
write函数
用write函数向打开的文件写数据
ssize_t write(int filedes, const void * buff, size_t nbytes) 若成功返回已写的字节数,若出错为-1
write出错的一个常见原因是磁盘已写满,或者操过了对一个给定进程的文件长度限制
io效率
对于unix内核而言文本文件和二进制文件并无区别
测试一个1428802的文件读写 buffer设置8192时其读写效率比较高
文件共享
unix支持在不同进程间共享打开文件
1、每个进程在进程表中都有一个记录项,每个记录项中有一张打开文件描述符表,可以将其视为一个矢量,每个描述符占用一项
与文件描述符相关联的是 文件描述符标识 指向一个文件表的指针
2、内核会为所有打开文件维持一张文件表,每个文件表项包含 文件状态标识 当前文件位移量 指向该文件v节点表项的指针
3、每个打开文件(或设备)都有一个v节点结构。v节点包含了文件类型和对此文件进行各种操作的函数的指针信息。v节点还包含了该文件的i节点。这些信息是在打开文件时从盘上读入内存的。i节点包含了文件的所有者、文件长度、文件所在的设备、指向文件在盘上所使用的实际数据块的指针等等。
当多个进程打开同一个文件时每个进程都单独对应一个文件表,而他们的v节点指针指向同一个v节点
当多个进程写同一个文件时,则可能产生逾期不到的结果
原子操作
任何一个要求多余1个函数调用的操作都不能成为原子操作,因为在两个函数调用之间,内核有可能会临时挂起该进程
unix提供了一种方法使这种操作成为原子操作,其方法就是在打开文件时设置O_APPEND标识。这就使得内核每次对这种文件进行写之前都将进程的当前位移量设置到文件的尾端,于是在每次写之前就不再需要调用lseek。
创建一个文件
在对open函数中O_CREAT和O_EXCL将文件的创建作为一个原子操作
int dup(int filedes) 和 int dup2(int filedes, int filedes2) 复制一个现存的文件描述符
dup返回新文件描述符一定是当前可用的文件描述符中的最小值。用dup2则可以用filedes2参数指定新描述符的数值。如果filedes2已经打开,则现将其关闭。若filedes等于filedes2 则dup2返回filedes2而不关闭它。这些函数返回的新文件描述符与参数filedes共享同一个文件表项。
fcntl
fcntl函数可以改变已经打开文件的性质
int fcntl(int filedes, int cmd, ....);
fcntl函数有五种功能:
- 复制一个现存的描述符(cmd = F_DUPFD)
- 获得/设置文件描述符标记(cmd = F_GETFD|F_SETFD)
- 获得或设置文件状态标识(cmd = F_GETFL|F_SETFL)
- 获得或设置异步io所有权(cmd=F_GETOWN|F_SETOWN)
- 获得或设置记录锁 (cmd = F_GETLK,F_SETLK,F_SETLKW)
F_DUPFD复制文件描述符,新文件描述符作为函数值返回。它是尚未打开的个描述符中大于或等于第三个参数值中各值的最小值。
F_GETFL 对应于filedes的文件状态标识作为函数值返回,文件状态如O_RDONLY O_WRONLY ...
F_SETFL设置文件状态 可更改为 O_APPEND O_NONBLOCK O_SYNC O_ASYNC
F_GETOWN 取当前接受SIGIO和SIGURG信号的进程ID或进程组ID
fnntl 的返回值与命令有关,如果出错统一返回-1
在unix中通常write只是将数据排入队列,而实际的io操作可能在以后的某个时刻进行。
程序运行时设置O_SYNC标识会增加时钟时间
ioctl函数
ioctl函数是io操作的杂物箱。
int ioctl(int filedes, int request, ...)
/dev/fd
打开文件/dev/fd/n等效于复制描述符n
某些系统提供路径名/dev/stdin /dev/stdout /dev/stderr 等效于/dev/fd/0 ...