第三章 文件IO[仅作学习笔记,其中可能有误解]
1. unix 系统中的IO是POSIX.1和Single UNIX SPecification中的一部分,不是ISO C的组成部分。
这一章的IO函数是不带缓冲的,与ISO(标准)C的带缓冲IO相对应。
2. 对于内核来讲,所有打开的文件都通过文件描述符[非负整数]来引用。当打开一个文件时,有内核返回一个描述符给进程。
3. 0与标准输入相关联 1与标准输出相关联 2与标准出错输出相关联
为避免幻数,用符号常量来替代:STDIN_FILENO STDOUT_FILENO STDERR_FILENO.
文件描述符为0~OPEN_MAX,则一个进程打开的文件描述符是有限的
4. 创建文件
#include <fcntl.h>
int open(const char * pathname, int oflag, ... /*mode_t mode*/);
// oflag: O_RDONLY O_WRONLY O_RDWR
// O_APPEND O_CREAT O_DSYNC O_EXCL O_NOCTTY
// O_NONBLOCK O_RSYNC O_SYNC O_TRUNC
// mode: S_IS[UG]ID, S_ISVTX, S_I[RWX](USR|GRP|OTH)
// *****************************************************
// 仅当在创建文件时才需要第三个参数
oflag 为各选项的或操作结合而成,其中前三个只能选其一,其他任选
要想判断一个文件是否存在,可同时使用O_CREAT和O_EXCL,若存在则会出错,否则创建
5. 创建文件
#include <fcntl.h>
int create(const char *pathname, mode_t mode);
// mode: S_IS[UG]ID, S_ISVTX, S_I[RWX](USR|GRP|OTH)
该函数相当于 open(pathname, O_WRONLY |O_CREAT | O_TRUNC, mode);
故限制性较大
6. 关闭文件
#include <unistd.h>
int close(int filedes);
6.1 关闭文件时会释放该进程加在该文件上所有的记录锁
6.2 进程结束时,内核会关闭进程打开的所有文件
7. 文件偏移
#include <unistd.h>
off_t lseek(int filedes, off_t offset, int whence);
7.1 off_t 与标准C中的fseek(FILE * fd, long offset, int whence);的long形成对比
7.2 管道、FIFO、网络套接字是不能设置偏移量的,会返回-1,并且设置errno为ESPIPE
7.3 偏移量有可能是负数,因此测试是否可设置偏移量时应该判断返回值是否为-1,而不是小于0
7.4 文件偏移量有可能大于当前文件长度,从而形成空洞,空洞不要求在磁盘上占用存储区
书中举了一个例子,两个长度相同的文件,无空洞的比有空洞的占用了更多的存储区
8. 读取数据
#include <unistd.h>
ssize_t read(int filedes, void * buf, size_t nbytes);
// 返回值:成功则返回读到的数据字节数,到尾部则0,出错-1
8.1 有多种情况实际读到的数据字节数比要求读的nbytes少:
+ 读普通文件,读到要求的字节数前已经到文件尾,下一次则返回0
+ 从终端读,通常一次一行
+ socket中的网络缓冲结构可能小于所要读的
+ 管道或FIFO中包含的字节比要求的少
+ 有信号中断
9. 写数据
#include <unistd.h>
ssize_t write(int filedes, const void * buf, size_t nbytes);
// 返回值:成功则返回已写的数据字节数,出错-1
9.1 返回值通常与nbytes相同,否则表示出错了,常见原因是磁盘满了,或超过文件长度限制
10 IO效率
每次调用read和write都是系统调用,用户态与核心态间的上下文切换是比较耗时的,我们应当选择
比较好的nbytes来保证IO的效率,测试的结果是当设置的缓冲区为4096[文件系统ext2的块大小]时,
效率已经很好,在增加缓冲区时间也没有太大影响
11. 文件共享
11.1 UNIX系统支持不同进程共享打开的文件。
11.2 内核使用了三种数据结构表示打开的文件,这也决定了文件共享的特点
+ 进程表 [fd|文件指针(指向文件表)]
+ 文件表 [文件状态标志|当前文件偏移量|v节点指针]
+ v节点 [v节点信息|i节点信息|当前文件长度]
11.3 两个独立的进程打开同一个文件
+ 此时进程表项指向不同的文件表,但文件表指向同一v节点,则inode
此时要保证共享文件能正确操作:
此时通过原子操作来保证,每次写完和设置文件偏移量会保证是在同一个原子操作,
从而保证正确共享
11.4 两个独立的进程打开同一个文件
#include <unistd.h>
int dup(int filedes);
#include <unistd.h>
int dup2(int filedes, int filedes2);
+ dup用于复制文件描述符号,dup2复制filedes到filedes2
+ 通过dup会使得进程表中有两个描述符使用同一文件表
如何处理:
+
12 文件系统与缓冲区高速缓存的一致性
12.1 内核中有缓冲区高速缓存或页面高速缓存,[开头说本章的IO是不带缓冲的,是指在用户态没缓冲吗?]大多数磁盘IO通过缓冲进行,当写文件数据时,内核将数据复制缓冲区,等待缓冲区满或内核需要重用缓冲区时候,才将该缓冲区排入输出队列,然后等待到队首,才进行磁盘IO--这叫延迟写。
12.2 为保证磁盘中实际文件与高速缓存的一致性,提供了如下函数:
+
#include <unistd.h>
int fsync(int filedes);
只对filedes有效,且等待实际磁盘操作完成。处理数据和文件属性。
+
#include <unistd.h>
int fdatasync(int filedes);
只对filedes有效,且等待实际磁盘操作完成。但只处理数据。
+
#include <unistd.h>
void sync(void);
只是将修改过的块缓冲区写入输出队列,不等实际磁盘操作。update守护进程每30调一次,sync 命令也是调用这个函数。
13. 改变已经打开文件的属性
13.1 提供的函数:
#include <fcntl.h>
int fcntl(int filedes, int cmd, ... /* int arg */);
// cmd: F_DUPFD, F_GETFD, F_SETFD, F_GETFL, F_SETFL
// F_GETOWN, F_SETOWN, F_GETLK, F_SETLK, F_SETLKW
这个函数很复杂,知道是处理已打开文件的属性,其他的选项用到再查吧。
FD_CLOEXEC表示在调用exec时文件描述符是否还有用。
fcntl用于记录锁
14. IO的其他操作
14.1 ioctl函数
#include <unistd.h>
#include <sys/ioctl.h>
#include <stropts.h>
int ioctl(int filedes, int request, ...);
现在已经有部分函数来替代iocntl了.这个函数通常是对设备[终端]的一些操作。
ioctl用于STREAMS I/O.
15. /dev/fd/n等价于文件描述符n
[转载请表明]http://blog.csdn.net/kangquan2008
[转载请表明] http://blog.csdn.net/kangquan2008