APUE学习: 第三章, 文件I/O

来源:互联网 发布:手绘漫画的软件 编辑:程序博客网 时间:2024/05/20 11:47

一, 基本函数 open, close, creat, lseek, read, write

  • 这些是最原始的系统内核函数, 其他I/O函数例如fread, fwrite系列等都是由此派生而出的
  • open函数:
    • int open(const char *pathname, int oflag, ...)
    • 有很多选项是内核版本相关的, 需要查询man手册
    • oflag 参数指定了打开模式, 必须是O_RDONLY, O_WRONLY和O_RDWR三选一, 以及若干其他选项, 指定了同步/异步, 阻塞/非阻塞, APPEND, TRUNC等功能
    • 当oflag中有O_CREAT时, 后面可以接一个mode参数, 用来指定其他用户对文件的读写权限
  • creat函数:  等效于open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode), 属于历史产物, 一般不会使用
  • close函数:
    • 会释放该进程加在文件上的所有记录锁
    • 进程终止时会自动关闭所有打开的文件
  • read函数:
    • ssize_t read (int fd, void * buf, size_t nbytes);
    • 返回读到的字节数, 可能有多种情况导致返回值小于nbytes参数, 如各种情况导致的数据不足
    • 返回0表示达到文件结尾, 返回-1出错
  • write
    • ssize_t write (int fd, const void * buf, size_t nbytes);
    • 成功则返回值等于nbytes参数,  不会出现返回值 < nbytes的情况
    • 出错返回-1, 一般是磁盘满
  • lseek:
    • off_t lseek (int fd, off_t offset, int whence)
    • whence是三者之一: SEEK_CUR, SEEK_END, SEEK_SET
    • 不产生I/O操作
    • 一般情况下每个fd有自己的偏移量. (dup或fork可能产生特殊情况)
    • 返回偏移量

二, Unix文件数据结构

  1. 当一个进程打开多个文件时, 示意图如下:

  • 每个fd对应一个记录项, 记录项中又一个文件表的指针, 文件表中又有一个指针指向真正的文件节点(v节点)
  • 不同的fd中可以指向相同的文件v节点, 也可能有相同的文件表指针
  • 这种结构使得可以在进程中打开多个文件(v节点相同), 而文件标志位和偏移量在文件表结构中, 与文件无关, 这样就使多个fd同时操作一个文件成为可能
  • v节点中包含了v节点和i节点, 其中v节点记录与具体文件系统无关的信息, i节点记录文件系统相关的信息. Linux中的v节点也用i节点数据结构实现
  • 可以用dup和dup2函数来复制一个现有的文件描述符(fd). 其中dup2可以指定新生成的fd的数值(如果fd被占用会关闭原来的). 新的fd与原fd共用一个文件表指针

三, 原子操作

  • 多进程写同步:
    • 当多个进程对一个文件进行写操作时, 实际需要两个操作: lseek到结尾 + write.  但是会有时序问题, O_APPEND选项可以解决这个问题, 即每次写时先lseek至结尾.  或者用pread和pwrite解决.
  • 延迟写问题:
    • 一般write操作会先写到一个缓存区中, 缓存区写满后会进入写队列, 当到达队列头时才开始真正的写IO操作. 而write函数早已返回(在数据拷贝至缓冲区完成). 这可能导致数据没有及时写入磁盘. 一些应用例如数据库等可能需要屏蔽掉这个功能
    • 可以通过sync() 或fsync()函数解决这个问题. sync会将所有缓冲区内容同步到磁盘. fsync会将指定文件的缓冲写入磁盘.
    • 也可通过一些文件标志位影响这种行为. 如O_SYNC, O_RSYNC和O_DSYNC等, 具体需查man手册

四, fcntl函数

  • int fcntl (int fd, int cmd, ...)
  • fcntl函数允许只根据fd就对文件属性进行读取/修改/复制, 包括各种标志, 权限和记录锁
  • 当使用F_SETFL改变标志位时要注意会覆盖掉原有标志位,  正确的做法是需要先get到原标志位, 再或上新标志位, 然后再进行set

五, 其他

  • ioctl函数:  对于一些特殊的设备, 如磁带等, 有一些操作无法通过以上函数描述, 就通过ioctl函数加上各种参数和标志位实现. 每种IO设备都可以指定自己的标志位
  • /dev/fd:   可以通过用路径的方式实现一些文件操作, 便于shell命令操作, 例如在命令行中指定标准输入可以用 /dev/fd/0表示

 

 


0 0