文件共享
Unix系统支持在不同进程间共享打开的文件
内核使用3种数据结构表示打开文件,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响
- 每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:
- 文件描述符标志
close_on_exec
- 指向一个文件表项的指针
- 文件描述符标志
- 内核为所有文件维持一张文件表。每个文件表项包含:
- 文件状态标志(读、写、同步、阻塞等)
- 当前文件偏移量
- 指向该文件v节点表项的指针
ps: 文件描述符标志和文件状态标志区别在于作用范围: 前者用于一个进程的一个描述符,后者应用于指向该给定文件表项的任何进程中的所有描述符
- 每个打开文件(设备)都有一个v节点(v-node)结构。这些信号是在打开文件时从磁盘上读入内存的
- v节点包含了文件类型和对此文件进行各种操作函数的指针。
- 对于大多数文件,v节点还包含了该文件的i节点(i-node,索引节点)。 i节点包含以下信息
- 文件所有者
- 文件长度
- 指向文件实际数据块在磁盘上所在位置的指针等
如果两个独立进程打开了同一个文件:
- 在完成每个
write
之后,在文件表项中的当前文件偏移量会增加所写入的字节数。如果这导致了当前文件偏移量超出了当前文件长度,则将i节点表项中的当前文件长度设置为当前文件偏移量 - 如果
O_APPEND
打开一个文件,则文件状态标志会被设置为O_APPEND
,当前文件偏移量会被设置为i节点中的当前文件长度,从而可以让每次write
的数据追加到文件末尾 - 如果
lseek
到文件末尾,当前文件偏移量会被设置为i节点中的当前文件长度 lseek
只修改文件表项中的当前文件偏移量,不进行任何IO操作
从上面可以看出,如果多个进程写同一个文件时,进程不安全,解决这个问题的方法有两个:
- 加锁
- 原子操作: 任何要求多于一个函数调用的操作都不是原子操作,因为在两个函数调用之间,内核可能会临时挂起进程