第三章 文件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