首页 > 技术文章 > 信息安全系统设计基础第九周学习总结

1551127024hwy 2015-11-08 21:24 原文

第十章 系统级I/O

10.1Unix I/O

所有的I/O设备,如网络、磁盘都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行。这种将设备映射为文件的方式,允许UNIX内核引出一个简单、低级的应用接口,称为UNIX I/O。

输入输出的执行方式:

打开文件:

打开文件,内核会返回描述符。标准输入(STDIN_FILENO)描述符为0、标准输出(STDOUT_FILENO)描述符为1、标准错误(STDERR_FILENO)描述符为2。

改变当前文件位置:

文件位置k,是文件开头起始的字节偏移量。

读写文件:

读是从文件拷贝到存储器。写相反。当k超过文件字节数m时,会触发end-of-file(EOF)条件。

关闭文件:

释放文件打开时创建的数据结构(释放文件的存储器资源),将描述符恢复到可用的描述符池中。

10.2打开和关闭文件

进程通过open函数来打开一个已存在的文件或者创建一个新文件的。

int open(char *filename, int flags, int mode);

flags参数指明了进程打算如何访问这个文件:

O_RDONLY:只读

O_WRONLY:只写

O_RDWR:可读可写

O_CREAT:如果文件不存在,就创建它的一个截断的(空)文件。

O_TRUNC:如果文件已经存在,就截断它。

O_APPEND:在每次写操作前,设置文件位置到文件的结尾处。

当进程通过带某个mode参数的open函数调用来创建一个新文件时,文件的访问权限位被设置为mode & ~umask。

进程通过调用close关闭一个打开的文件。

10.3 读和写文件

返回值-1表示一个错误,而返回值0表示EOF。否则,返回值表示的时实际传送的字节数量。

include <unistd.h>

ssize_t read(int fd, void *buf, size_t n);   //返回:若成功则为0,若出错则为-1.

写文件

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

include

ssize_t write(int fd, const void *buf, size_t n);   //返回:若成功则为写的字节数,若出错则为-1.

读时遇到EOF

假设该文件从当前文件位置开始只含有20个字节,而应用程序要求我们以50个字节的片进行读取,这样一来,这个read的返回的值是20,在此之后的read则返回0.

从终端读文本行

如果打开的文件是与终端相关联的,那么每个read函数将一次传送一个文本行,返回的不足值等于文本行的大小。(具体的含义可看我以前的文章,关于缓冲区的)

读和写网络套接字(socket)

如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会导致read和write返回不足值。

10.3 读和写文件 598

read 执行输入(读)
write执行输出(写)
read 函数:从fd的当前文件位置 拷贝 最多n个字节到存储器位置 buf。
 返回值-1表示错误,0表示EOF,否则表示实际传送的字节数。
 write 同理。
不足值:read和write传送的字节数少于应用程序的要求。
读时遇到了EOF
从终端读文本行:如果打开的文件是与终端相联的,那么每个read函数将一次传送一个文本行,不足值=文本行大小。
 读和写网络套接字。

10.4 用rio包健壮地读写 599

rio_readn函数从描述符fd的当前文件位置最多传送n个字节到存储器位置usrbuf。类似的rio_writen函数从位置usrbuf传送n个字节到描述符fd。rio_readn函数在遇到EOF时只能返回一个不足值。rio_writen函数绝不会返回不足值。

注意:如果rio_readn和rio_writen函数被一个从应用信号处理程序的返回中断,那么每个函数都会手动地重启read或write。

一个文本行就是一个由 换行符 结尾的ASCII码字符序列。在Unix系统中,换行符是‘\n’,与ASCII码换行符LF相同,数值为0x0a。假设我们要编写一个程序来计算文本文件中文本行的数量应该如何来实现呢?

一种方法是用read函数来一次一个字节地从文件传送到用户存储器,检查每个字节来查找换行符。

这种方法的问题就是效率不高,每次取文件中的一个字节都要求陷入内核。

一种更好的方法是调用一个包装函数(rio_readlineb),它从一个内部缓冲区拷贝一个文本行,当缓冲区变空时,会自动的调用read系统调用来重新填满缓冲区。

10.5 读取文件元数据

应用程序能通过调用stat和fstat函数,检索到关于文件的信息(元数据)。stat函数以一个文件名作为输入,fstat函数以文件描述符作为输入。

st _ size成员包含了文件的字节数大小。st _ mode成员则编码了文件访问许可位和文件类型。

普通文件包括某种类型的二进制或文本数据。

目录文件包含关于其他文件的信息。

套接字是一种用来通过网络与其他进程通信的文件。

10.6 共享文件

内核用三个相关的数据结构来表示其打开的文件。

描述符表:表项由进程打开的文件描述符来索引的,每个打开的描述符表指向文件表中的一个表项,每个进程有其独立的描述符表。

文件表:打开文件的集合是由一张文件表来表示的,所有的进程共享这张表。包括:当前的文件位置、引用计数、以及一个指向v-node表中对应表项的指针。

v-node表:每个表项包含stat结构中的大多数信息,;包括st_mode和st_size成员,所有进程共享。

10.8 标准I/O

标准I/O库:一组高级输入输出函数。将一个打开的文件模型化为一个流,一个流即一个指向FILE类型的结构的指针。每个ANSI C程序开始时都有三个打开的流:stdin(标准输入),stdout(标准输出),stderr(标准错误)。

类型为FILE的流是对文件描述符和流缓冲区的抽象。为了减小系统开销。

10.9 综合:该使用哪些I/O函数

大多数时候使用标准I/O就可以了。

在网络套接字的时候使用RIO函数。需要格式化输出,使用sprintf函数格式化一个字符串,然后用rio_writen把它发送到套接口。格式化输入,使用rio_readlineb读一个完整的文本行,再使用scanf从文本行提取不同字段。

 

 

推荐阅读