3.1. Introduction
3.2. File Descriptor
To the kernel, all open files are referred to by file descriptors.
对于内核而言,所有打开的文件都通过文件描述符引用。 When we open an existing file or create a new file, the kernel returns a file descriptor to the process. 当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。3.3. open Function
3.4. creat Function
3.5. close Function
3.6. lseek Function
An open file's offset can be set explicitly by calling lseek.
#includeoff_t lseek(int filedes, off_t offset, int whence);
The interpretation of the offset depends on the value of the whence argument.
If whence is SEEK_SET, the file's offset is set to offset bytes from the beginning of the file. If whence is SEEK_CUR, the file's offset is set to its current value plus the offset. The offset can be positive or negative. If whence is SEEK_END, the file's offset is set to the size of the file plus the offset. The offset can be positive or negative.Because a successful call to lseek returns the new file offset, we can seek zero bytes from the current position to determine the current offset:off_t currpos; currpos = lseek(fd, 0, SEEK_CUR);
This technique can also be used to determine if a file is capable of seeking. If the file descriptor refers to a pipe, FIFO, or socket, lseek sets errno to ESPIPE and returns 1.
这种方法也可用来确定所涉及的文件是否可以设置偏移量。如果文件描述符引用的是一个管道、FIFO或网络套接字,则lseek返回-1,并将errno设置为ESPIPE#include "apue.h" int main(void) { if (lseek(STDIN_FILENO, 0, SEEK_CUR) == -1) printf("cannot seek\n"); else printf("seek OK\n"); exit(0); }
If we invoke this program interactively, we get
$ ./a.out < /etc/motd seek OK $ cat < /etc/motd | ./a.out cannot seek $ ./a.out < /var/spool/cron/FIFO cannot seek
3.7. read Function
3.8. write Function
3.9. I/O Efficiency
3.10. File Sharing
3.11. Atomic Operations
Consider a single process that wants to append to the end of a file. Older versions of the UNIX System didn't support theO _APPEND option to open, so the program was coded as follows:
if (lseek(fd, 0L, 2) < 0) /* position to EOF */ err_sys("lseek error"); if (write(fd, buf, 100) != 100) /* and write */ err_sys("write error");
This works fine for a single process, but problems arise if multiple processes use this technique to append to the same file. (This scenario can arise if multiple instances of the same program are appending messages to a log file, for example.)
Assume that two independent processes, A and B, are appending to the same file. Each has opened the file but without the O_APPEND flag. This gives us the same picture as Figure 3.7. Each process has its own file table entry, but they share a single v-node table entry. Assume that process A does the lseek and that this sets the current offset for the file for process A to byte offset 1,500 (the current end of file). Then the kernel switches processes, and B continues running. Process B then does the lseek, which sets the current offset for the file for process B to byte offset 1,500 also (the current end of file). Then B calls write, which increments B's current file offset for the file to 1,600. Because the file's size has been extended, the kernel also updates the current file size in the v-node to 1,600. Then the kernel switches processes and A resumes. When A calls write, the data is written starting at the current file offset for A, which is byte offset 1,500. This overwrites the data that B wrote to the file. The problem here is that our logical operation of "position to the end of file and write" requires two separate function calls (as we've shown it). The solution is to have the positioning to the current end of file and the write be an atomic operation with regard to other processes. Any operation that requires more than one function call cannot be atomic, as there is always the possibility that the kernel can temporarily suspend the process between the two function calls (as we assumed previously).The UNIX System provides an atomic way to do this operation if we set the O_APPEND flag when a file is opened. As we described in the previous section, this causes the kernel to position the file to its current end of file before each write. We no longer have to call lseek before each write.
3.12. dup and dup2 Functions
3.13. sync, fsync, and fdatasync Functions
3.14. fcntl Function
3.15. ioctl Function
The ioctl function has always been the catchall for I/O operations. Anything that couldn't be expressed using one of the other functions in this chapter usually ended up being specified with an ioctl. Terminal I/O was the biggest user of this function.