进程中的编号
在Linux中,open 成功后返回一个较小的非负整数,作为进程文件描述符表中的索引。后续 read、write、lseek 等操作使用这个编号引用已打开文件,而不必每次重新提交路径名。open 通常分配当前最低的空闲编号;关闭后,该编号可以被再次使用。
编号的意义依赖进程及其当前状态。两个进程都持有描述符3,不代表它们访问同一对象;同一进程先后两次得到3,也可能对应不同文件。路径名参与查找,而打开后保留的是引用:路径随后被移除或改指其他文件,并不会自动把已有描述符转向新目标。
参考:[1]
打开文件描述与共享状态
文件描述符与“打开文件描述”不是同一层。后者保存文件偏移量和文件状态标志,描述符则引用它。分别 open 同一路径通常建立不同的打开文件描述;dup 则给已有引用增加一个新编号,两个编号仍指向同一个打开文件描述。
例如,描述符3复制为4后,通过3改变普通文件的当前位置,通过4看到的也是这个位置。复制并没有创建文件内容的副本,也没有得到独立的读写游标。共享的是偏移量及文件状态标志;描述符自身的标志,例如关闭时机相关的 close-on-exec 标志,需要另行区分。
标准I/O与重定向
程序启动时,标准输入、标准输出、标准错误所关联的描述符通常分别为0、1、2。它们常连接终端,但也可由父进程安排为文件或其他资源,因此“标准输出”不等于固定的屏幕设备。C库中的 stdin、stdout、stderr 是 FILE 流接口,并不是三个整数变量。
dup2 可把指定的目标描述符改为引用另一描述符的打开文件描述,因而是实现重定向的重要机制。例如把一个已打开输出文件的描述符复制到1,随后对描述符1的写入就使用该文件。目标原已打开时,关闭旧引用和复用编号作为一个原子操作完成,避免把两步拆开时的编号竞争。
关闭引用而非保证数据落盘
close 释放一个描述符,使该编号可被复用;若仍有其他描述符引用同一打开文件描述,关闭其中一个并不等于把其他引用一并关闭。只有相应引用都不再需要时,底层状态才能被释放。这个区别也说明,不能把旧编号保存下来长期当作文件的永久身份。
在Linux中,close 成功不保证所有数据已经持久写入磁盘;持久性需要考虑 fsync 等接口及存储条件。关闭错误应被检查,却不能不加区分地重试同一个整数,因为编号可能早已释放并被其他线程复用。可移植程序还须查阅目标系统对中断及错误的具体规定,不能假定各系统的 close 失败语义完全一致。
参考:[3]