文章目录
系统级I/O
输入/输出 I/O是在主存和外部设备之间复制数据的过程。
- 输入操作是从I/O设备复制数据到主存
- 输出操作是从主存复制数据到I/O设备
文件:无格式的字节序列
打开文件:一个应用程序通过要求内核打开相应的文件,来宣告应用程序想要访问一个I/O设备。内核返回一个当前进程中没有打开的最小描述符(非负整数),应用程序在后续对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。
当文件偏移量 > 文件大小的时候,触发end-of-file(EOF),在文件结尾处并没有明确的“EOF符号”。(判断文件是否已经结束是通过比较当前的文件偏移量和文件长度来实现)。
关闭文件:当应用程序完成了对文件的访问之后,应用程序就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源。
文件
文件类型:每个Linux文件都有一个类型(7种文件类型)
- 普通文件,应用程序通常要区分文本文件和二进制文件,对内核而言两者没有区别
- 文本文件
只含ASCII或Unicode字符的普通文件。Linux文本文件包含了一个文本行序列,其中每一行都是一个以换行符结尾的字符序列。 - 二进制文件
其它文件
- 文本文件
- 目录:是包含一组链接的文件,其中每个链接都将一个文件名映射到一个文件
- 套接字
- 命名通道
- 符号链接
- 字符
- 块设备
作为其上下文的一部分,每个进程都有一个当前工作目录来确定进程在目录层次结构中的当前位置。
#include <unistd.h>
char *getcwd(char *buf, size_t size);
成功:以NULL结尾的字符串,包含绝对路径
出错:NULL
文件操作
打开文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(char *filename, int flags, mode_t mode);
成功:新文件描述符,总是在进程中当前没有打开的最小描述符
失败:-1
flags参数指明了进程打算如何访问这个文件:flags可以是一个或者更多位掩码的或
- O_RDONLY
- O_WRONLY
- O_RDWR
- O_CREATE:如果文件不存在,就创建它的一个截断的空文件
- O_TRUNC:如果文件已存在,就截断它
- O_APPEND:在每次写操作之前,设置文件的偏移量到文件末尾处
mode参数指定了新创建文件的访问权限位:
作为上下文的一部分,每个进程都有一个umask(可通过调用umask函数来设置)。当进程通过带某个mode参数的open函数调用来创建一个新进程时,文件的访问权限被设置为mode & ~mask
。
关闭一个打开的文件:
#include <unistd.h>
int close(int fd);
成功:0
res = 0
出错:-1
res = -1, Bad file descriptor
关闭一个已关闭的描述符会出错(返回-1,并设置errno)。
errno = 0时,strerror(errno) = “Success”
读和写文件:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t n);
成功:读的字节数
EOF:0
出错:-1
ssize_t write(int fd, const void *buf, size_t n);
成功:写的字节数
出错:-1
lseek可以显示修改当前文件的位置
某些情况下,read和write传送的字节比应用程序要求的要少,这些不足值(返回值 < 参数n)不表示有错误,出现不足值的情况如下:
- 读时遇到EOF,所要读的参数n > 文件当前位置开始到EOF的字节数
- 从终端读文本行
- 读和写网络套接字
※RIO健壮地读写
可以通过反复调用read和write处理不足值,直到所有需要的字节都传送完毕。
Robust I/O,自动处理不足值。
RIO的无缓冲的输入输出函数
这些函数直接在内存和文件之间传送数据,没有应用级缓冲,对将二进制数据读写到网络和从网络读写二进制数据尤其有用。
通过调用rio_readn和rio_writen函数,app可以在内存和文件之间传送数据。
/*
rio_readn函数从描述符fd的当前文件位置最多传送n个字节到内存位置usrbuf
遇到一个EOF时只能返回一个不足值 */
ssize_t rio_readn(int fd,