Unix 系统I/O总结

看完了apue关于系统I/O的几章,打算还是总结一下,不然在脑子总是不成条理.

  1. 文件描述符:对于内核而言,所有打开的文件都是通过文件描述符引用的.(所以系统调用的I/O函数参数基本都是文件描述符,而标准I/O库函数却不是这样).

    1. 文件描述符是一个非负整数,其中0,1,2已经与标准输入,标准输入,标准错误关联(默认情况下,实际上我们也可以修改,但是并不推荐).
    2. 当打开现有文件或者创建一个新文件的时候,内核向进程返回一个文件描述符.
  2. 函数open(),openat(),打开或创建一个文件

    int open(const char *path, int oflag, mode_t mode)
    int openat(int fd, const char *path, int oflag, mode_t mode)

    • path参数是要打开的或创建的文件的名字
    • oflag用来说明多个选项,用多个选项’或’运算构成oflag.下面列出了部分oflag选项,具体可查看Unix程序员手册.
      • O_RDONLY 只读打开
      • O_WRONLY 只写打开
      • O_RDWR 读,写打开
      • O_APPEND 每次写都追加到文件尾端
      • O_CREAT 若文件不存在就创建它.使用此选项时需要指定mode参数来确定创建文件的权限位(权限位还与umask值有关)
      • O_TRUNC 如果此文件存在,且为只写或读-写成功打开,将其长度截断为0
    • 若成功返回相应的文件描述符,否则返回-1
    • fd参数区分开open和openat函数,有三种情况:
      1. path指定的是绝对路径,fd就被忽略,此时openat等同于open
      2. path指定相对路径名,fd参数指定相对路径名在文件系统中的开始位置.fd参数是通过打开相对路径名所在的目录来获取的.
      3. path指定相对路径名,fd参数为特殊值AF_FDCWD.此时,路径名在当前工作目录中火区,openat类似于open函数
  3. creat(),close()函数

    int creat(const char *path, mode_t mode)
    int close(int fd)

    1. creat函数等效于open(path,O_WRONLY | O_CREAT | O_TRUNC, mode).实际上更适合用open来替代
    2. close成功返回0,creat成功返回只写打开的文件描述符,错误的话都返回-1
    3. 关闭一个文件会释放该进程加在该文件上的所有记录锁,同时当一个进程终止时,内核自动关闭其打开的所有文件,所以close实际上可以不调用,但是建议显式调用,是个良好的变编程习惯(笑)
  4. lseek()函数

    off_t lseek(int fd, off_t offset, int whence)

    1. 若成功,返回新的文件偏移量,若错误就返回-1
    2. 通常,读写从当前文件偏移量开始,并使偏移量增加所读写的字节.默认情况下,若open没有指定O_APPEND选项,偏移量被设为0
    3. 对offset的解释和whence有关:
      • 若whence是SEEK_SET,则将文件偏移量设为距文件开始处offset个字节处
      • 若是SEEK_CUR,则设置为距当前文件偏移量offset个字节处
      • 若是SEEK_END,则设置为文件长度加上offset,offset可正可负(注:在offset为正的情况下,下次写开始之处与之前文件结束之处就存在了空洞)
  5. read()函数

    ssize_t read(int fd, void *buf, size_t nbytes)

    1. 成功的话返回实际读到的字节数,如果到达文件尾就返回0,出错返回-1
    2. 读操作从当前文件偏移量开始,读至多nbytes的字节,如果提前遇到文件尾,返回实际读到的字节数,下次读遇到文件尾直接返回0.
  6. write()函数

    ssize_t write(int fd, const void *buf, size_t nbytes)

    1. 成功的话返回已写的字节数,出错返回-1,通常其返回值与nbytes相同,否则表示出错.
    2. 对于普通文件,写操作从当前偏移量开始. 如果打开该文件时,指定了O_APPEND选项,则在每次写操作前,将文件偏移量设置在文件的当前结尾处. 在一次成功写之后,文件偏移量增加实际写的字节数
  7. 文件共享

    1. 每个进程在进程表中有一个记录项,记录项中包含一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项.与每个文件描述符相关联的是
      • 文件描述符标志(close_on_exec)
      • 指向一个文件表项的指针
    2. 内核为所有打开文件维持一张文件表(每个文件可能不止一张),每个文件表项包含:
      • 文件状态标志(读,写,添加,同步,非阻塞等)
      • 当前文件偏移量
      • 指向该文件v节点表项的指针
    3. 每个打开文件(或设备)都有一个v节点结构.v节点包含了文件类型和对此文件进行各种操作函数的指针,对大多数文件来说,v节点还包含了该文件的i节点(索引节点).这些信息实在打开文件时从磁盘读入内存的,所以文件的所有相关信息都是随时可用的.
  8. dup()函数和dup2()函数

    int dup(int fd)
    int dup2(int fd, int fd2)

    1. 如果成功返回新的文件描述符,错误返回-1
    2. 由dup返回的新文件描述符一定是当前可用描述符中的最小值.
    3. 对于dup2,可以由参数fd2指定新描述符的值.如果fd2已经打开,则先将其关闭.若fd等于fd2,则dup2返回fd2,而不关闭它.否则fd的FD_CLOEXEC文件描述符被清楚,这样fd2在进程调用exec(一系列的函数)时是打开状态.
    4. 这些函数返回的新文件描述符与参数fd共享一个文件表项,如下图所示.
  9. fcntl()函数

    int fcntl(int fd, int cmd, …/* int arg */)

    1. 若成功,返回值依赖于cmd,若失败返回-1
    2. 返回有有一些五种功能(具体用法略显复杂,可以参阅Unix程序员手册):
      • 复制一个现有的描述符(cmd = F_DUPED或 F_DUPFD_CLOEXEC)
      • 获取/设置文件描述符标志(cmd = F_GETED或F_SETFD)
      • 获取/设置文件状态标志(cmd = F_GETFL或F_SETFL)
      • 获取/设置异步I/O所有劝(cmd = F_GETOWN或F_SETOWN)
      • 获取/设置记录锁(cmd = F_GETLK或F_SETLK或F_SETLKW)
    3. 注意:在修改文件描述符标志或文件状态标志时必须谨慎,要先获得现在的标志值,然后按照期望修改它,最后设置新标志值.不能只是执行F_SETFD或F_SETFL,这样会关闭之前设置的标志位

结语:算是把apue第三章的内容copy了一遍(笑),上面简单总结了我觉得比较重要的部分,太过繁琐的细节我就没有细扣了,想深入了解的可以看看unix程序员手册.上面也主要是系统I/O的部分,文件和标准库I/O的也还没总结,过几天应该会再写几篇博客总结一下.