On Unix systems, why do we have to explicitly `open()` and `close()` files to be able to `read()` or `write()` them?
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
).
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.
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.