// Need to specify mode if file is being created...
int open(const char *path, int oflag, mode_t mode);
// ...otherwise, mode argument is omitted.
int open(const char *path, int oflag);
oflag
must specify a file access mode optionally bitwise-or’d with other
options – see man 2 open
.mode
specifies UNIX file permissions via a bitwise-or of user/group/other
bit masks – see man chmod
.Creates an entry in the process’s file descriptor table and returns a file descriptor (the index of the entry in the table). The entry stores the current offset into the file, the open options, and other metadata.
Example (taken from man open
in Linux):
#include <fcntl.h>
int fd;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
char *pathname = "/tmp/file";
fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
pathname
for writing only (O_WRONLY
) and delete the contents of the
file if any (O_TRUNC
). Create the file with mode
if it doesn’t already exist
(O_CREAT
).Another example – creating a lock file for distributed synchronization (e.g. multi-process webserver):
fd = open("/var/run/myprog.pid", O_WRONLY | O_CREAT | O_EXCL, 0644);
O_EXCL
: error if O_CREAT
and the file exists.Note the use of octal notation – 0644
corresponds to S_IRUSR | S_IWUSR |
S_IRGRP | S_IROTH
ls -l
: rw-r--r--
Redundant: creat(path, mode)
is equivalent to open(path,
O_WRONLY | O_CREAT | O_TRUNC, mode)
And it should have been called create
, says Ken Thompson
int close(int fildes);
Deletes the file descriptor table entry at index fildes
.
off_t lseek(int fildes, off_t offset, int whence);
SEEK_SET
, the file offset shall be set to offset bytes.SEEK_CUR
, the file offset shall be set to its current location plus offset.SEEK_END
, the file offset shall be set to the size of the file plus offset.Note that lseek()
doesn’t actually do file I/O – it only modifies the file
table entry.
ssize_t read(int fildes, void *buf, size_t nbyte);
Returns number of bytes read, 0 if end of file, -1 on error. Number of bytes
read may be less than the requested nbyte
– check return value.
read()
may block forever on a “slow” read from pipes, FIFOs (aka named pipes),
sockets, or keyboard.
For sockets, read(socket, buf, nbyte)
is equivalent to recv(socket, buf, nbyte, 0)
:
ssize_t recv(int socket, void *buffer, size_t length, int flags)
recv()
blocks until it has received at least 1 bytessize_t write(int fildes, const void *buf, size_t nbyte);
Returns number of bytes written, -1 on error. Number of bytes written may be less than the requested nbyte
(e.g. filling up a disk).
write()
may block forever on a “slow” write into pipes, FIFOs, or sockets
(e.g. see discussion on blocking in man 7 pipe
).
For sockets, write(socket, buf, nbyte)
is equivalent to
send(socket, buf, nbyte, 0)
ssize_t send(int socket, const void *buffer, size_t length, int flags)
send()
blocks until it sends all bytes requestedAtomicity (from APUE 3.12): given an operation with multiple steps, either all steps are performed (on success) of none are performed (on failure). Not possible to observe a subset of steps performed.
If the file was opened with O_APPEND
flag, the file offset gets set to the end
of the file prior to each write
lseek()
then write()
).FILE *fopen(const char *pathname, const char *mode); // open()
int fclose(FILE *stream); // close()
int fseek(FILE *stream, long offset, int whence); // lseek()
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); // read()
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); // write()
Note that FILE *stream
replaces int fd
– standard I/O is based on streams.
FILE *
is an “opaque object” – not meant to be inspected, just passed into API.
int fd
, buffer pointer/metadata, stream status, etc.Goal: reduce number of read()
/write()
syscalls while performing stream operations.
fread()
/fwrite()
call read()
/write()
once in a while, then use underlying buffer.Trace syscall invocations via strace
:
io.c
vs std-io.c
– note how many times read()
is called in each programKernel data structures for open files
Three key data structures:
Two independent processes with the same file open
(e.g. fork-then-open.c
)
Independent file table entries, but they point to the same inode
Kernel data structures after dup(1)
New file descriptor table slot points to the same file table entry.
Sharing of open files between parent and child after fork
(e.g. open-then-fork.c
)
Similar to dup()
– child file descriptor table is constructed as if it
were dup()
‘d – references parent file table entries.
Last updated: 2022-01-25