深入理解计算机系统第10章 系统级I/O

第10章 系统级I/O

在某些重要情况下,使用高级I/O函数不太可能。例如,标准I/O库没有提供读取文件元数据的方式,如文件大小或文件创建时间。另外,I/O库还存在一些问题,使用它来进行网络编程非常冒险。

10.1 Unix I/O

所有的I/O设备,如网络、磁盘和终端,都被模型化为文件,而所有的输入和输出都被当做相应文件的读和写。

  • 打开文件
  • 改变当前的文件位置:文件位置是从文件开头起始的字节偏移量.
  • 读写文件
  • 关闭文件:无论进程以何种原因终止,内核都会关闭所有打开的文件并释放它们的存储器资源.

10.2 打开和关闭文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(char *filename, int flags, mode_t mode);  返回:若成功则为新的文件描述符,否则为-1

flags参数:

  • O_RDONLY: 只读
  • O_WRONLY: 只写
  • O_RDWR: 可读可写
  • O_CREAT: 若文件不存在,则创建
  • O_TRUNC: 若文件已存在,就截断它.
  • O_APPEND: 追加

mode参数: 指定新文件的访问权限位

关闭文件:

#include <unistd.h>

int close(int fd);  返回:若成功则为0,若出错为-1

10.3 读和写文件

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t n);

ssize_t write(int fd, const void *buf, size_t n);

read函数: 从描述符为fd的当前文件位置拷贝最多n个字节到存储器位置buf,返回值-1表示错误,返回值0表示EOF, 否则,返回实际传送的字节数量.

write函数: 从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置.

某些情况,read和write传送的字节比应用程序要求的要少,出现原因如下:

  • 读时遇到EOF
  • 从终端读文本行: 若打开文件是与终端相关联的,如键盘和显示器,那么每个read函数将一次传送一个文本行,返回值为文本行的大小.
  • 读和写网络套接字(socket): 如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会引起read和write返回不足值. 对unix管道调用read和write时,也有可能引起不足值.

10.4 RIO包健壮地读写(略)

10.5 读取文件元数据

可调用stat和fstat函数,检索到关于文件的信息.

#include <unistd.h>
#include <sys/stat.h>

int stat(const char *filename, struct stat *buf);
int fstat(int fd, struct stat *buf);
                  返回:若成功则为0,出错则为-1

stat函数以文件名作为输入,而fstat以文件描述符作为输入.
stat数据结构
st_size成员包含文件的字节数大小,st_mode成员编码了文件访问许可位和文件类型。

Unix提供宏指令来根据st_mode成员来判断文件类型.

宏指令描述
S_ISREG()是否为普通文件
S_ISDIR()是否为目录文件
S_ISSOCK()是否为网络套接字

10.6 共享文件

内核用相关的数据结构来表示打开的文件:

  • 描述符表: 每个进程都有它独立述符表,每个打开的描述符表项指向文件表中的一个表项。
  • 文件表: 打开文件的集合由一张文件表来表示,所有的进程共享这张表。每个文件表的表项包括当前的文件位置,引用计数(即当前指向该表项的描述符表项数),以及一个指向v-node表中对应表项的指针。关闭一个文件描述符会减少相应的文件表表项中的引用计数。内核不会删除这个文件表表项,直到它的引用计数为0.
  • v-node表: 所有进程共享v-node表。每个表项包含相应打开文件的属性信息,包括stat结构的的大部分信息,诸如st_mode和st_size成员.

图10-11是典型的打开文件的内核数据结构,描述符1和4通过不同的打开文件表表项来引用两个不同的文件。没有共享文件。
典型的打开文件的内核数据结构
多个描述符也可以通过不同的文件表表项来引用同一个文件,如图10-12所示。例如,如果以同一个filename调用open函数两次,就会发生这种情况。关键思想是每个描述符都有它自己的文件位置,所以对不同描述符的读操作可以从文件的不同位置获取数据.
文件共享
对于fork的情况: 假设fork前,父进程有图10-11所示的打开文件。图10-13展示了调用fork之后的情况。子进程有一个父进程描述符的副本。父子进程共享相同的打开文件表几个,因此共享相同的文件位置。因而,在内核删除相应文件表表项之前,父子进程必须都关闭了它们的描述符.
子进程如何继承父进程的打开文件

10.7 I/O重定向

Unix Shell提供了I/0重定向操作符,允许用户将磁盘文件和标准输入输出联系起来。如,

unix>  ls > foo.txt

使得外壳加载和执行ls程序,将标准输出重定向到磁盘文件foo.txt。

Unix I/O提供dup2函数支持重定向

#include <unistd.h>

int dup2(int oldfd, int newfd);
        返回: 若成功则为非负的描述符,出差则为-1

dup2拷贝描述符表表项oldfd到描述符表表项newfd,覆盖描述符表表项newfd以前的内容。若newfd已经打开,dup2会在拷贝oldfd之前关闭newfd.

调用dup2(4,1)之前,初始状态如图10-11所示,调用之后如图10-14所示.
调用dup2后的内核数据结构

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值