zoukankan      html  css  js  c++  java
  • 文件IO

    一、打开文件的内核数据结构

    内核使用3种数据结构表示打开文件:进程表项、文件表项、V节点表项

    如图1所示:

     图1. 打开文件的内核数据结构

    • 进程表项(process table entry)

    每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述符表

    文件描述符表由文件描述符(fd)索引,对于内核而言,所有打开文件都通过文件描述符引用。

    按照惯例,文件描述符0与进程的标准输入关联,文件描述符1与进程的标准输出关联,文件描述符2与进程的标准错误关联。

    • 文件描述符

    每个文件描述符,索引文件描述符表中的一个矢量。

    文件描述符表中的每一项包括:文件描述符标志(fd flags)、指向文件表项的指针(file pointer)

    • 文件描述符标志

    文件描述符标志包括FD_CLOEXEC(close-on-exec)标志:

    如果设置FD_CLOEXEC标志,那么对应的fd在执行exec后时会失效。

    一般fork后的子进程和父进程共用文件描述符表,但在exec后,子进程就不能使用设置了FD_CLOEXEC标志的fd(close-on-exec)。

    • 文件表项(file table entry)

    文件表项包括:文件状态标志、当前文件偏移量、指向该文件V节点表项的指针。

    • 文件状态标志(file status flags)

    文件状态标志如图2所示,其中前5个互斥,后半部分可以附选。

    图2. 文件状态标志 

    • 当前文件偏移量(current file offset)

    当前文件偏移量,记录对该文件操作的具体位置,如从哪个位置开始读,从哪个位置开始写。

    如O_APPEND就标志在文件结尾进行操作。

    • v节点表项(v-node table entry)

     v节点包含文件类型和对此文件进行各种操作函数的指针,还包含了该文件的i节点。

    i节点包含了文件的所有者、文件长度、指向文件实际数据块在磁盘上所在位置的指针等。

    • 共享文件

    如果两个进程各自打开了同一个文件,则数据结构关系如图3所示。

    图3. 共享文件

     每个进程都有自己的文件表项,这是因为每个进程都有独特的对该文件的当前偏移量。

     二、Open和Openat

    图4. open和openat函数

    path是要打开或创建文件的名字、oflag参数说明此函数的多个选项、...表明余下的参数及其类型是可变的(仅在创建新文件时,才用到这个参数)。

    返回文件描述符fd。

    • oflag标志

    图2的文件状态标志,在这里都可以设置,设置的文件状态标志(file status flags)。

    其余:

    O_CLOEXEC,设置文件描述符标志(fd flags)->FD_CLOEXEC(close-on-exec)标志。

    O_CREAT,文件不存在则创建文件,需要指定mode。

    O_EXCL,如果指定了O_CREA,而文件已经存在,则出错。

    • open和openat的区别

    fd参数把open和openat函数区分开,共有3中可能性。

    (1)path参数是绝对路径,fd参数被忽略,openat和open功能一致

    (2)path参数是相对路径,fd参数标识开始地址。

    (3)path参数是相对路径,fd是特殊值AT_FDCWD,open和openat功能类似

    三、creat

    图5. creat函数

    creat函数创建一个新的文件,此函数等效于:open(path, O_WRONLY  | O_CREAT | O_TRUNC, mode);

    creat的不足是它只能以只写的方式打开创建的文件,在提供open的新版本前,如果要创建一个临时文件,并要先写后读该文件,

    则必须先调用creat、close、open。

     

    四、close

     关闭一个打开文件:

    图5. close函数

     当一个进程终止时,内核自动关闭它所有的打开文件。很多程序利用这个特性,而不显式地用close关闭打开文件。

    五、lseek

    图6. lseek函数

    lseek函数更改的是图1文件表项中的当前文件偏移量。

    空洞:

    文件偏移量可以大于文件的当前长度,在这种情况下对文件的下一次写将加长该文件,并在文件中构成一个空洞

    但是文件空洞,不占用磁盘存储区。

    大多数平台提供两组接口以处理文件偏移量,一组用32位文件偏移量,一组用64位文件偏移量,可以用sysconf函数确定支持哪种环境。

    六、read、write、I/O效率

    图7. read函数

    图8. write函数

    预读技术改善性能, Buffer Size大到一定程度,再增加缓冲区长度则I/O效率提升不明显。

    七、dup和dup2

    图9. dup和dup2函数

     dup和dup2都可用来复制一个现有的文件描述符。

    dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。

    dup2可以用fd2制定新文件描述符的值,如果fd2已经打开,则先关闭。如果fd=fd2,则直接返回,不关闭fd2。

    效果如下:

     

    图10. dup后的内核数据结构

    dup2等效于

    close(fd2);

    fcntl(fd, F_DUPFD, fd2);

    但是dup2是原子操作,原子操作指的是由多步组成的一个操作,要么一步也不执行,要么执行完所有步骤,不可能执行所有步骤的一个子集。

    八、sync、fsync、fdatasync

    图11. sync、fsync、fdatasync

    sync将所有修改过的块缓冲区排入写队列,不等待实际写磁盘操作结束。

    fsync只对fd指定的文件起作用,并且等待磁盘写操作结束后才返回。

    fdatasync与fsync类似,但只影响文件的数据部分,而fsync还会同步文件的属性。

    九、fcntl

    图12. fcntl函数

    fcntl函数有5种功能。

    (1)复制一个已有的fd(cmd = F_DUPFD 或 F_DUPFD_CLOEXEC)

    与dup、dup2等类似。

    (2)获取/设置文件描述符标志(cmd = F_GETFD 或 F_SETFD)

     文件描述符标志FD_CLOEXEC

     (3)获取/设置文件状态标志(cmd = F_GETFL 或 F_SETFL)

     即更改图1文件表项中的文件状态标志

    (4)获取/设置异步I/O所有权(cmd = F_GETOWN 或 F_SETOWN)

    设置/获取当前接收SIGIO和SIGURG信号的进程ID或进程组ID

    (5)获取/设置记录锁(cmd = F_GETLK、F_SETLK或F_SETLKW)

    十、ioctl

    ioctl函数是I/O操作的杂物箱,不能用之前的函数表示的I/O操作通常都能用ioctl表示。

    图13. ioctl函数

  • 相关阅读:
    8月份的To-Do List
    Block作为返回值时的使用
    Block作为参数时的使用
    AFNetwork 作用和用法详解
    UIScrollView监听静止的数种情况
    UIAppearance
    自定义Log实现条件编译
    事件的基本概念
    ubuntu16.04下安装MySQL
    在Ubuntu16.04下搭建samba,实现linux与windows之间的资源共享
  • 原文地址:https://www.cnblogs.com/songdechiu/p/10261694.html
Copyright © 2011-2022 走看看