所有执行 I/O 操作的系统调用都以文件描述符,一个非负整数(通常是小整数),来指代打开的文件。文件描述符用以表示所有类型的已打开文件,包括管道(pipe)、 FIFO、 socket、终端、设备和普通文件。针对每个进程,文件描述符都自成一套。
执行文件 I/O 操作的 4 个主要系统调用
4.1.1 open
fd = open(pathname, flags, mode) 函数打开 pathname 所标识的文件,并返回文件描述符,用以在后续函数调用中指代打开的文件。如果文件不存在, open()函数可以创建之,这取决于对位掩码参数 flags 的设置。 flags 参数还可指定文件的打开方式:只读、只写亦或是读写方式。 mode 参数则指定了由 open()调用创建文件的访问权限,如果 open()函数并未创建文件,那么可以忽略或省略 mode 参数。
4.1.2 read
numread = read(fd, buffer, count) 调用从 fd 所指代的打开文件中读取至多 count 字节的数据,并存储到 buffer 中。 read()调用的返回值为实际读取到的字节数。如果再无字节可读(例如:读到文件结尾符 EOF 时),则返回值为 0。
4.1.3 write
numwritten = write(fd, buffer, count) 调用从 buffer 中读取多达 count 字节的数据写入由fd 所指代的已打开文件中。 write()调用的返回值为实际写入文件中的字节数,且有可能小于 count。
4.1.4 close
status = close(fd)在所有输入/输出操作完成后,调用 close(),释放文件描述符 fd 以及与之相关的内核资源。
复制文件的一个案例fileio/copy.c
#include <sys/stat.h> #include <fcntl.h> #include "tlpi_hdr.h" #ifndef BUF_SIZE /* Allow "cc -D" to override definition */ #define BUF_SIZE 1024 #endif int main(int argc, char *argv[]) { /* 自定义源文件和目标文件的文件描述符 */ int inputFd, outputFd, openFlags; mode_t filePerms; ssize_t numRead; char buf[BUF_SIZE]; if (argc != 3 || strcmp(argv[1], "--help") == 0) usageErr("%s old-file new-file ", argv[0]); /* 打开源文件和目标文件 */ inputFd = open(argv[1], O_RDONLY); if (inputFd == -1) errExit("opening file %s", argv[1]); openFlags = O_CREAT | O_WRONLY | O_TRUNC; filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* rw-rw-rw- */ outputFd = open(argv[2], openFlags, filePerms); if (outputFd == -1) errExit("opening file %s", argv[2]); /** 将从源文件读到的数据写入目标文件 * read的返回值等于实际读到的字节数,到再无可读,numRead=0 * write的返回值等于实际写入字节数,不一定等于numRead * 以下两种情况会退出 * 1.读写到末尾 * 2.出现错误 * 关闭inputFD/outputFD失败都会返回-1 * */ while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0) if (write(outputFd, buf, numRead) != numRead) fatal("write() returned error or partial write occurred"); if (numRead == -1) errExit("read"); if (close(inputFd) == -1) errExit("close input"); if (close(outputFd) == -1) errExit("close output"); exit(EXIT_SUCCESS); }