Why do open() and close() exist in the Unix filesystem design?

Couldn't the OS just detect the first time read() or write() was called and do whatever open() would normally do?

  • 23
    It's worth noting that this model is not part of the filesystem but rather of the Unix API. The filesystem is merely concerned with where on disk the bytes go and where to put the filename, etc. It would be perfectly possible to have the alternative model you describe on top of a Unix filesystem like UFS or ext4, it would be up to the kernel to translate those calls into the proper updates for the filesystem (just as it is now). – marcelm Feb 25 '16 at 15:03
  • 19
    As phrased, I think this is more about why open() exists. "Couldn't the OS just detect the first time read() or write() and do whatever open() would normally do?" Is there a corresponding suggestion for when closing would happen? – Joshua Taylor Feb 25 '16 at 15:26
  • 8
    How would you tell read() or write() which file to access? Presumably by passing the path. What if the file's path changes while you're accessing it (between two read() or write() calls)? – user253751 Feb 25 '16 at 19:33
  • 3
    Also you usually don't do access control on read() and write(), just on open(). – Pavel Šimerda Feb 25 '16 at 20:00
  • 6
    @Johnny: You're perhaps forgetting just how limited the hardware was in those days. The PDP-7 on which Unix was first implemented had (per Google) a maximum 64K of RAM and a 0.333 MHz clock - rather less than a simple microcontroller these days. Doing such garbage collection, or using system code to monitor file access, would have brought the system to its knees. – jamesqf Feb 26 '16 at 5:31

Dennis Ritchie mentions in «The Evolution of the Unix Time-sharing System» that open and close along with read, write and creat were present in the system right from the start.

I guess a system without open and close wouldn't be inconceivable, however I believe it would complicate the design. You generally want to make multiple read and write calls, not just one, and that was probably especially true on those old computers with very limited RAM that UNIX originated on. Having a handle that maintains your current file position simplifies this. If read or write were to return the handle, they'd have to return a pair -- a handle and their own return status. The handle part of the pair would be useless for all other calls, which would make that arrangement awkward. Leaving the state of the cursor to the kernel allows it to improve efficiency not only by buffering. There's also some cost associated with path lookup -- having a handle allows you to pay it only once. Furthermore, some files in the UNIX worldview don't even have a filesystem path (or didn't -- now they do with things like /proc/self/fd).

| improve this answer | |
  • 7
    The cost of path lookup and permission checking etc. etc. is very significant. If you wanted to make a system without open/close, you'd be sure to implement stuff like /dev/stdout to allow piping. – Peter Cordes Feb 25 '16 at 11:37
  • 5
    I think another aspect to this is that you can keep that handle to the same file when using multiple reads when you keep the file open. Otherwise, you could have cases where another process unlinks and re-create a file with the same name, and reading a file in chunks could effectively be completely incoherent. (Some of this may depend on the filesystem too.) – Bruno Feb 25 '16 at 15:07
  • 2
    I designed one without close(); you pass the inode number and offset to read() and write(). I can't do without open() very easily because that's where name resolution lives. – Joshua Feb 25 '16 at 21:43
  • 3
    @Joshua: Such a system has fundamentally different semantics because unix file descriptors do not refer to files (inodes) but to open file descriptions, of which there may be many for a given file (inode). – R.. GitHub STOP HELPING ICE Feb 26 '16 at 18:39
  • @Joshua, you just renamed open() to get_inode() and made the whole system more rigid (impossible to read/write the same file at several positions simultaneously). – vonbrand Feb 29 '16 at 12:04

Then all of the read and write calls would have to pass this information on each operation:

  • the name of the file
  • the permissions of the file
  • whether the caller is appending or creating
  • whether the caller is done working with the file (to discard unused read-buffers and ensure write-buffers really finished writing)

Whether you consider the independent calls open, read, write and close to be simpler than a single-purpose I/O message is based on your design philosophy. The Unix developers chose to use simple operations and programs which can be combined in many ways, rather than a single operation (or program) which does everything.

| improve this answer | |
  • Callers would also in most cases have to specify the desired offset within a file. There are some situations (e.g. a UDP protocol that allows access to data) where having each request independently identify a file and offset may be helpful since it eliminates the need for a server to maintain state, but in general it's more convenient to have the server keep track of file position. Further, as noted elsewhere, code which is going to write files often needs to lock them beforehand and lock them afterward; combing those operations with open/close is very convenient. – supercat Feb 26 '16 at 16:20
  • 5
    The "file" may not have a name or permissions in the first place; read and write aren't restricted to files that live on a file system, and that is a fundamental design decision in Unix, as pjc50 explains. – reinierpost Feb 26 '16 at 17:29
  • 1
    Also where in the file to read/write it - the beginning, the end, or an arbitrary position (to typically be immediately after the end of the last read/write) - the kernel keeps track of this for you (with a mode to direct all writes to the end of the file, or otherwise files are opened with the position at the beginning and advanced with each read/write and can be moved with lseek) – Random832 Feb 26 '16 at 21:36

The concept of the file handle is important because of UNIX's design choice that "everything is a file", including things that aren't part of the filesystem. Such as tape drives, the keyboard and screen (or teletype!), punched card/tape readers, serial connections, network connections, and (the key UNIX invention) direct connections to other programs called "pipes".

If you look at many of the simple standard UNIX utilities like grep, especially in their original versions, you'll notice that they don't include calls to open() and close() but just read and write. The file handles are set up outside the program by the shell and passed in when it is started. So the program doesn't have to care whether it's writing to a file or to another program.

As well as open, the other ways of getting file descriptors are socket, listen, pipe, dup, and a very Heath Robinson mechanism for sending file descriptors over pipes: https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux-socket

Edit: some lecture notes describing the layers of indirection and how this lets O_APPEND work sensibly. Note that keeping the inode data in memory guarantees the system won't have to go and fetch them again for the next write operation.

| improve this answer | |
  • 1
    Also creat, and listen doesn't create an fd, but when (and if) a request comes in while listening accept creates and returns an fd for the new (connected) socket. – dave_thompson_085 Feb 25 '16 at 10:41
  • 18
    This is THE correct answer. The famous (small) set of operations on file descriptors is a unifying API for all kinds of resources which produce or consume data. This concept is HUGELY successful. A string could conceivably have a syntax defining the resource type together with the actual location (URL anybody?), but to copy strings around which occupy several percent of the available RAM (what was it on the PDP 7? 16 kB?) seems excessive. – Peter - Reinstate Monica Feb 25 '16 at 16:39
  • Perhaps it would be, if the low-level calls and the shell were developed at the same time. But pipe was introduced a few years after development on Unix started. – Thomas Dickey Feb 26 '16 at 22:42
  • 1
    @Thomas Dickey: Which merely shows how good the original design was, since it allowed the simple extension to pipes &c :-) – jamesqf Feb 27 '16 at 6:44
  • But following that line of argument, this answer provides nothing new. – Thomas Dickey Feb 27 '16 at 12:54

The answer is no, because open() and close() create and destroy a handle, respectively. There are times (well, all of the time, really) where you may want to guarantee that you are the only caller with a particular access level, as another caller (for instance) writing to a file that you are parsing through unexpectedly could leave an application in an unknown state or lead to a livelock or deadlock, e.g. the Dining Philosophers lemma.

Even without that consideration, there are performance implications to be considered; close() allows the filesystem to (if it is appropriate or if you called for it) flush the buffer that you were occupying, an expensive operation. Several consecutive edits to an in-memory stream are much more efficient than several essentially unrelated read-write-modify cycles to a filesystem that, for all you know, exists half a world away scattered over a datacenter worth of high-latency bulk storage. Even with local storage, memory is typically many orders of magnitude faster than bulk storage.

| improve this answer | |

Open() offers a way to lock files while they are in use. If files were automatically opened, read/written and then closed again by the OS there would be nothing to stop other applications changing those files between operations.

While this can be manageable (many systems support non-exclusive file access) for simplicity most applications assume that files they have open don't change.

| improve this answer | |

Because the file's path might move while you're assuming it will stay the same.

| improve this answer | |

Reading and writing to a filesystem may involve a large variety of buffering schemes, OS housekeeping, low-level disk management, and a host of other potential actions. So the actions of open() and close() serve as the set-up for these types of under the hood activities. Different implementations of a filesystem could be highly customized as needed and still remain transparent to the calling program.

If the OS didn't have open/close, then with with read or write, those file actions would still have to perform any initializations, buffer flushing/management, etc each and every time. That's a lot of overhead to impose for repetitive reads and writes.

| improve this answer | |
  • Not to forget that open() and close() keeps also the position in file (for next read or next write). So at the end or the read() and write() would need a struct to handle all parameters, or it need arguments for each parameter. Creating a structure is equivalent (programmer site) to a open, so if OS also know about open, we have only more advantages. – Giacomo Catenazzi Feb 27 '16 at 8:19

The Unix mantra is "offer one way of doing things", which means "factoring" into (reusable) pieces to be combined at will. I.e., in this case separate the creation and destruction of file handles from their use. Important benefits came later, with pipes and network connections (they are also manipulated through file handles, but they are created in other ways). Being able to ship file handles around (e.g. passing them to child processes as "open files" which survive an exec(2), and even to unrelated processes through a pipe) are only possible this way. Particularly if you want to offer controlled access to a protected file. So you can e.g. open /etc/passwd for writing, and pass that to a child process that isn't allowed to open that file for writing (yes, I know this is a ridiculous example, feel free to edit with something more realistic).

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.