Unix I/O
Opening files. An application announces its intention to access an I/O device by asking the kernel to open the corresponding file. The kernel returns a small nonnegative integer, called a descriptor, that identifies the file in all subsequent operations on the file. The kernel keeps track of all information about the open file. The application only keeps track of the descriptor.
Each process created by a Linux shell begins life with three open files: standard output(descriptor 0), standard output(descriptor 1), and standard error(descriptor 2). The header file (unistd.h> defines constants STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO, which can be used instread of explicit descriptor values.
Changing the current file position.
The kernel maintains a file position k, initially 0, for each open file. The file position is a byte offset from the beginning of a file. An application can set the current file positon k explicitly by performing a seek operation.
Reading and writing files. A read operation copies n > 0 bytes from a file to memory, starting at the current file position k and then incrementing k by n. Given a file with a size of m bytes, performing a read operation when k >= m triggers a condition known as end-of-file (EOF), which can be detected by the application. There is no explicit "EOF character" at the end of a file.
Similarly, a write operation copies n > 0 bytes from memory to a file, starting at the current file position k and then updating k.
Closing files.
When an application has finished accessing a file, it informs the kernel by asking it to close the file. The kernel responds by freeing the data structures it created when file was opened and restoring the descriptor to a pool of available descriptors. When a process terminates for any reason, the kernel closes all open files and frees their memory resources.
10.2 Files
A regular file contains arbitrary data. Application programs often distinguish between text files, which are regular files that contain only ASCII or Unicode characters, and binary files, which are everything else. To the kernel there is no difference between text and binary files.
A Linux text file consists of a sequence of text line, where each line is a sequence of Characters terminated by a newline character('\n'). The newline character is the same as the ASCII line feed character(LF) and has a numeric value of 0x0a.
A directory is a file consisting of an array of links
where each link maps a filename to a file, which may be another directory. Each directory contains at least two entries: .(dot) is a link to the directory itself, and ..(dot-dot) is a link to the parent directory in the directory hierarchy. You can create a directory with the mkdir command, view its contents with ls, and delete it with rmdir.
A socket is a file that is used to communicate with another process across a network.
Other file types include named pipes, symbolic links, and character and block devices, which are beyond our scope.
The Linux kernel organizes all files in a single directory hierarchy anchored by the root directory named / (slash). Each file in the system is a direct or indirect descendant of the root directory, Figure 10.1 shows a portion of the directory hierarchy on our Linux system.
Figure 10.1
As part of its context, each process has a current working directory that identifies its current location in the directory hierarchy. You can change the shell's current working directory with the cd command.
Locations in the directory hierarchy are specified by pathnames. A pathname is a string consisting of an optional slash followed by a sequence of filenames separated by slashes. Pathnames have two forms:
- An absolute pathname starts with a slash and denotes a path from the root node. For example, in Figure 10.1, the absolute pathname for hello.c is /home/droh/hello.c
A relative pathname starts with a filename and denotes a path from the current working directory. For example, in Figure 10.1, if home/droh is the current working directory, then the relative pathname for hello.c is ./hello.c.
10.3 Opening and closing files
A process opens an existing file or creates a new file by calling the open function.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(char *filename, int flags, mode_t mode);
The open function converts a filename to a file descriptor and returns the descriptor number. The descriptor returned is always the smallest descriptor that is not currently open in the process. The flags argument indicates how the process intends to access the file:
O_RDONLY. Reading only
O_WRONLY Writing only
O_RDWR Reading and writing
For example, here is how to open an existing file for reading:
fd = Open("foo.txt", O_RDONLY, 0);
The flags argument can also be ORed with one or more bit masks that provide additional instructions for writing:
O_CREAT. If the file doesn't exist, then create a truncated (empty) version of it.
O_TRUNC if the file already exists, then truncate it.
O_APPEND Before each write operation, set the file position to the end of the file.
For example, here is how you might open an existing file with the intent of appending some data:
fd = Open("foo.txt", O_WRONLY | O_APPEND, 0);
The mode argument specifies the access permissions bits of new files. The symbolic names for these bits are shown in Figure 10.2
As part of its context, each process has a umask that is set by calling the umask function. When a process creates a new file by calling the open function with some mode argument, then the access permission bits of the file are set to mode & ~umask. For example, suppose we are given the following default values for mode and umask:
define DEF_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
define DEF_UMASK S_IWGRP | S_IWOTH
Then the following code fragment creates a new file in which the owner of the file has read and write permissions, and all other users have read permissions:
umask(DEF_UMASK);
fd = Open("foo.txt", O_CREAT | O_TRUNC | O_WRONLY, DEF_MODE);
Finally, a process closes an open file by calling the close function.
#include <unistd.h>
int close(int fd);
10.4 Reading and writing files Appications perform input and output by calling the read and write functions, respectively.
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t n);
ssize_t write(int fd, const void *buf, size_t n);
The read function copies at most n bytes form the current file position of descriptor fd to memory location buf. A return value of -1 indicates an error, and a return value of 0 indicates EOF. Otherwise, the return value indicates the number of bytes that were actually transferred.
The write function copies
网友评论