输入/输出(I/O)是在主存和外部设备之间复制数据的过程。
10.1 Unix IO
所有的IO设备都被模型化文件,而所有的输入和输出都被当做对相应文件的读和写。
打开文件。应用程序通过要求内核打开相应的文件。内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件。内核记录有关打开文件的所有信息,应用程序只需记住这个描述符。
Linux shell 创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0),标准输出(描述符为1)和标准错误(描述符为2)
应用程序能够通过执行seek操作显式地设置文件的当前位置为k。
一个读操作就是从文件复制n个字节到内存,从当前文件位置k开始。给定一个大小为m字节的文件,当k>=m时执行读操作会触发EOF。
关闭文件。应用通知内核关闭文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。
10.2 文件
1.普通文件。包含任意数据,应用程序常常要区分文本文件和二进制文件,文本文件是只含有ASCII或Unicode字符的普通文件。二进制文件是所有其他的文件。对内核而言,文本文件和二进制文件没有区别。
2.目录。一组包含链接的文件。其中每个链接都将一个文件名映射到一个文件。
. 是到该目录自身的链接
.. 是到目录层次结构中父目录的链接。
mkdir 创建目录,ls查看其内容,rmdir删除该目录。
3.套接字,是用来与另一个进程进行跨网络通信的文件。
Linux内核将所有文件都组织成一个目录层次结构。由名为/的根目录决定。
作为上下文的一部分,每个进程都有一个当前工作目录。可以用cd修改。
10.3 打开和关闭文件
int open(char *filename,int flags,mode_t mode);
返回的描述符总是在进程中当前没有打开的最小描述符。
flags指明进程打算如何访问:
O_RDONLY, O_WRONLY, O_RDWR
flags参数也可以为写提供额外指示:
O_CREAT: 如果文件不存在就创建一个截断的文件。
O_TRUNC:如果文件已经存在就截断它。
O_APPEND:每次写操作前设置文件位置到文件结尾处。
mode参数指定了新文件的访问权限位。作为上下文的一部分,每个进程都有一个umask,他是通过调用umask函数来设置的。文件的访问权限被设置为mode&~umask。
10.4 读和写文件
ssize_t read(int fd,void *buf,size_t n);
ssize_t write(int fd,const void *buf,size_t n);
size_t 定义为unsigned long, ssize_t(有符号的大小)被定义为long。read定义为有符号大小因为出错时他必须返回-1。
在某些情况下,read和write传送的字节比要求的少,这些不足值(short count)不表示有错误。出现原因有:
1. 读时遇到EOF。
2.从终端读文本行。
3.读和写网络套接字。
10.5 用RIO包
略。
10.6 读取文件元数据
应用程序能够通过调用stat和fstat函数检索到关于文件的信息(有时称为文件元数据)。
int stat(const char*filename,struct stat *buf);
int fstat(int fd,struct stat *buf);
10.7 读取目录内容
DIR *opendir(const char* name);
返回指向目录流的指针。流是对条目有序列表的抽象,在这里指目录项的列表。
struct dirent *readdir(DIR *dirp);
返回指向流dirp中下一个目录项的指针。
10.8 共享文件
父子进程共享相同的打开文件表集合,因此共享相同的文件位置。
10.9 IO重定向
int dup2(int oldfd,int newfd);
复制描述符表项oldfd到描述符表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd以及打开了,dup2会在复制oldfd之前关闭newfd。