在Unix系统上,为什么我们必须显式地`open()`和`close()`文件才能对其进行read()或write()?


50

为什么open()close()在Unix文件系统设计存在吗?

操作系统是否无法检测到第一次read()或被write()调用,并且open()通常会做什么?


22
值得注意的是,该模型不是文件系统的一部分,而是Unix API的一部分。文件系统仅关心字节在磁盘上的位置以及文件名的放置位置等。完全有可能在UFS或ext4之类的Unix文件系统上使用您描述的替代模型,具体取决于内核将这些调用转换为文件系统的适当更新(就像现在一样)。
marcelm '16

18
就像我所说的那样,我认为这更多是关于为什么open()存在。 “操作系统难道不能只检测第一次read()或write()并执行open()通常会做的事情吗?” 是否有关于何时关闭的相应建议?
约书亚·泰勒

7
您如何知道read()write()访问哪个文件?大概是通过路径。如果在您访问文件时(两个read()或两次write()调用之间)文件的路径改变了怎么办?
user253751 '16

2
同样,您通常不对和进行访问控制,read()write()仅对进行访问控制open()
PavelŠimerda'16

6
@Johnny:您也许忘记了当时的硬件有多有限。最初在Unix上实现的PDP-7(每个Google)具有最大64K的RAM和0.333 MHz的时钟-如今已经不像一个简单的微控制器那么多了。进行此类垃圾回收,或使用系统代码监视文件访问,将使系统崩溃。
jamesqf

Answers:


60

丹尼斯里奇提到了«共享时间的Unix系统的演变»openclose一起readwrite并且creat存在于系统从一开始。

我猜想openclose如果没有这样的系统,那将是无法想象的,但是我相信它将使设计变得复杂。通常,您需要进行多个读取和写入调用,而不仅仅是一个,这在UNIX起源于RAM十分有限的那些旧计算机上可能尤其如此。拥有可保持当前文件位置的手柄可简化此操作。如果readwrite要返回句柄,他们必须返回一对-句柄和自己的返回状态。该对的句柄部分对于所有其他呼叫都将是无用的,这会使该安排尴尬。将游标的状态留给内核不仅可以通过缓冲来提高效率。与路径查找相关联的还有一些成本-拥有手柄只能让您支付一次。此外,UNIX世界观中的某些文件甚至没有文件系统路径(或没有-现在它们处理诸如之类的东西/proc/self/fd)。


7
路径查找和权限检查等的成本非常高。如果要创建一个不带open/ 的系统close,则一定要实现/dev/stdout允许管道的功能。
彼得·科德斯

5
我认为这是另一方面,当您保持打开状态时,使用多次读取时,可以将该句柄保留在同一文件中。否则,您可能会遇到其他进程取消链接并重新创建具有相同名称的文件的情况,而分块读取文件实际上可能是完全不一致的。(其中一些可能也取决于文件系统。)
Bruno

2
我设计的没有close(); 您将inode编号和偏移量传递给read()和write()。我很难没有open(),因为名字解析就是在那里。
约书亚

3
@Joshua:这样的系统在语义上有着根本的不同,因为unix文件描述符不是引用文件(索引),而是打开文件描述,对于给定的文件(inode)可能有很多。
R.,

@Joshua,您刚刚重命名open()get_inode(),并使整个系统更加严格(无法同时在多个位置读取/写入同一文件)。
vonbrand '16

53

然后,所有readwrite调用都必须在每个操作中传递此信息:

  • 文件名
  • 文件的权限
  • 呼叫者是追加还是创建
  • 调用者是否已完成文件的使用(以丢弃未使用的读取缓冲区并确保写入缓冲区真正完成写入)

无论你认为独立的呼叫 openreadwriteclose比I / O单用途简单的消息是根据你的设计理念。Unix开发人员选择使用可以以多种方式组合的简单操作和程序,而不是使用一个单一的操作(或程序)来完成所有工作。


在大多数情况下,调用者还必须在文件中指定所需的偏移量。在某些情况下(例如,允许访问数据的UDP协议),让每个请求独立标识文件和偏移量可能会有所帮助,因为它消除了服务器维护状态的需要,但总的来说,拥有服务器更为方便跟踪文件位置。此外,如其他地方所述,要写入文件的代码通常需要事先锁定它们,然后再锁定它们。将这些操作与打开/关闭结合在一起非常方便。
超级猫

5
“文件”可能没有名字或权限。readwrite不仅限于住一个文件系统上的文件,这是在Unix系统中的基本的设计决定,因为pjc50解释。
reinierpost

1
同样在文件中读取/写入文件的位置-开始,结束或任意位置(通常在最后一次读取/写入结束之后)-内核会为您跟踪此操作(采用以下方式:将所有写操作都定向到文件的末尾,否则文件将以开头的位置打开,并随每次读/写操作前进,并且可以通过进行移动lseek
Random832 '16

51

文件句柄的概念很重要,因为UNIX的设计选择是“一切都是文件”,包括不属于文件系统的内容。例如磁带驱动器,键盘和屏幕(或电传打字机!),打孔卡/磁带读取器,串行连接,网络连接,以及(UNIX的主要发明)直接连接到称为“管道”的其他程序。

如果查看许多简单的标准UNIX实用程序(例如)grep,尤其是原始版本的UNIX实用程序,您会注意到它们不包含对open()和的调用,close()而仅包含readwrite。文件句柄由外壳程序在程序外部设置,并在启动时传递。因此,该程序不必关心它是要写入文件还是写入另一个程序。

还有open,让文件描述符的其他方式是socketlistenpipedup,和一个非常荒地鲁宾逊机制,在管道发送文件描述符:https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux -插座

编辑:一些讲义,描述了间接层以及这如何使O_APPEND合理地工作。请注意,将inode数据保留在内存中可确保系统不必再为下一次写操作再次获取它们。


1
另外creat,并且listen不会创建fd,而是在侦听时(如果有)请求进入时accept为新的(连接的)套接字创建并返回fd。
dave_thompson_085 '16

18
这是正确的答案。文件描述符上著名的(小型)操作集是用于产生或使用数据的所有资源的统一API。这个概念非常成功。字符串可能具有定义资源类型以及实际位置(URL有人吗?)的语法,但是要在字符串周围复制占据可用RAM百分之几的字符串(在PDP 7上是什么?16 kB?)似乎是多余的。 。
彼得-恢复莫妮卡

如果低级调用和外壳程序是同时开发的,则可能会这样。但是pipe是在Unix上的开发开始几年后才引入的。
Thomas Dickey

1
@Thomas Dickey:这仅显示了原始设计的出色之处,因为它允许对管道&c进行简单的扩展:-)
jamesqf

但是按照这一论点,这个答案没有提供任何新内容。
Thomas Dickey

10

答案是否定的,因为open()和close()分别创建和销毁了一个句柄。在某些时候(嗯,一直都是这样),您可能想保证自己是唯一具有特定访问级别的调用者,因为另一个调用者(例如)写入意外解析的文件可能会离开处于未知状态的应用程序或导致活动锁或死锁,例如Dining Philosophers引理。

即使没有考虑,也会影响性能。close()允许文件系统(如果适当或需要的话)刷新正在占用的缓冲区,这是一项昂贵的操作。对内存流的几次连续编辑要比对文件系统的几个本质上不相关的读写修改循环的效率要高得多,文件循环众所周知,分散在一个数据中心中的世界已经分散了一半,而该数据中心具有高延迟的大容量存储。即使使用本地存储,内存通常也比大容量存储快多个数量级。



5

因为在您假定文件路径保持不变时,文件的路径可能会移动。


4

读取和写入文件系统可能涉及多种缓冲方案,操作系统内务管理,低级磁盘管理以及许多其他可能的操作。所以的行动open()close()作为设置为这些类型的发动机罩下的活动。文件系统的不同实现可以根据需要进行高度自定义,并且对于调用程序仍然保持透明。

如果操作系统没有打开/关闭,则使用read或时write,这些文件操作仍然每次都必须执行任何初始化,缓冲区刷新/管理等操作。重复读取和写入会产生很多开销。


不要忘记open()和close()还会在文件中保留位置(用于下一次读取或下一次写入)。因此,最后,read()和write()将需要一个结构来处理所有参数,或者每个参数都需要参数。创建一个结构相当于一个开放结构(程序员站点),因此,如果OS也知道开放结构,那么我们只有更多优势。
Giacomo Catenazzi

1

Unix的口头禅是“提供一种做事方式”,这意味着“分解”为(可重用的)碎片以随意组合。即,在这种情况下,将文件句柄的创建和销毁与其使用分开。后来,管道和网络连接带来了重要的好处(它们也可以通过文件句柄进行操作,但是它们是通过其他方式创建的)。exec(2)只有这样,才能够传送文件句柄(例如,将文件句柄作为“打开文件”传递给子进程,这些进程在管道中存活,甚至通过管道传递给无关的进程)。特别是如果您想提供对受保护文件的受控访问。所以你可以例如打开/etc/passwd 进行编写,并将其传递给不允许打开该文件进行编写的子进程(是的,我知道这是一个荒谬的示例,请随意使用更实际的东西进行编辑)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.