信息安全系统设计基础第九周学习总结——20135308
第十章 系统级I/O
10.1 unix i/o 596
一个Unix文件就是一个m个字节的序列,所有的I/O设备都被模型化为文件,而所有的输入和输出都被当做对应文件的读和写来执行。这被称为UnixI/O,使得所有的输入和输出能以一种统一且一致的方式来执行:
1、打开文件
应用程序向内核发出请求→要求内核打开相应的文件→内核返回文件描述符
Unix外壳创建的每个进程开始时都有三个打开的文件:
标准输入——0(STDIN_FILENO) 标准输出——1(STDOUT_FILENO) 标准错误——2(STDERR_FILENO)
2、改变当前的文件位置
对于每个打开的文件,内核保持着一个文件位置k,初始为0。这个文件位置是从文件开头起始的字节偏移量。通过执行seek操作设置文件位置为k。
3、读写文件
(1)读操作
读操作就是从文件拷贝n>0个字节到存储器,并且改变文件当前位置。(如果当前位置是k,则改变为k+n) EOF的来源: 文件结尾处没有明确的EOF信号,是当文件当前位置的数值超过了文件大小时,会处罚一个称为end-of-file的条件,能够被应用程序检测到,这就是所谓的EOF信号。
(2)写 写操作是从存储器拷贝n>0个字节到一个文件,然后更新当前文件位置。
10.2 打开和关闭文件
- open函数将filename转换为一个文件描述符,并且返回描述符数字。
打开一个已存在的文件或者创建一个新文件:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(char *filename,int flags,mode_t mode) (若成功则返回新文件描述符,若出错为-1)
-
flags参数指明了进程如何访问文件,常见取值:
ORDONLY:只读 OWRONLY:只写 ORDWR:可读可写 一个或者更多位掩码的或 OCREAT:文件不存在,就创建新文件 OTRUNC:如果文件存在,就截断它 OAPPEND:写操作前设置文件位置到结尾处
-
mode:指定了新文件的访问权限位,符号名字如下所示:
-
关闭一个打开的文件:
#include <unisted.h> int close(int fd)(若成功则为0,若出错则为-1)
返回值:成功返回0,出错返回-1 关闭一个已经关闭的描述符会出错
fd:即文件的描述符。
10.3 读和写文件
-
读文件:read函数从描述符为fd的当前文件位置拷贝最多n个字节到存储器位置buf。返回值表示实际传送的字节数量,错误返回-1,EOF返回0。
#include <unistd.h> ssize_t read(int fd, void *buf, size_t n);
-
写文件:write函数从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置。
#include <unistd.h> ssize_t write(int fd, void *buf, size_t n);
-
通过调用lseek函数,应用程序可以显示地修改当前文件的位置。
-
出现不足值(指在某些情况下,read和write传送的字节比应用程序要求的要少)的原因:
1.读的时候遇到EOF:文件末尾剩余的字节数不足读取文件的字节片大小。
2.从终端读文本行:若打开文件与终端相关联,则每个read函数将一次传送一个问本行。返回的不足值等于文本行的大小。
3.读和写socket:若打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会引起read和write返回不足值。
10.4 用RIO包健壮地读写
1、RIO的无缓冲的输入输出函数
rioreadn函数和riowriten函数,应用程序可以在存储器和文件之间直接传送数据:
#include "csapp.h" ssize_t rio_readn(int fd, void *usrbuf, size_t n); ssize_t rio_writen(int fd, void *usrbuf, size_t n);
若成功则返回传送成功的字节数,若EOF则为0(只对rio_readn而言),若出错 则为-1。
2.RIO的带缓冲的输入函数
rio_ readlineb和rio_readnb函数从一个内部读缓冲区拷贝一个文本行,当缓冲区变空时,会自动调用read重新填满缓冲区。
void rio_readinitb(rio_t *rp,int fd);(无返回) ssize_t rio_readlineb(rio_t *rp,void *usrbuf,size_t maxlen); ssize_t rio_readnb(rio_t *rp,void *usrbuf,size_n);
若成功则返回传送成功的字节数,若EOF则为0,若出错 则为-1。
打开每一个描述符都会调用一次rioreadinitb函数,他将描述符fd和地址rp处的一个类型为riot的读缓冲区联系起来。
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
st_size:包含文件的字节数大小
st_mode:包编码文件访问许可位和文件类型。
-
普通文件包括某种类型的二进制或文本数据。
-
目录文件包含关于其他文件的信息。
-
套接字是一种用来通过网络与其他进程通信的文件。
10.6 共享文件
内核用三个相关的数据结构来表示其打开的文件:
-
描述符表:表项由进程打开的文件描述符来索引的,每个打开的描述符表指向文件表中的一个表项,每个进程有其独立的描述符表。
-
文件表:打开文件的集合是由一张文件表来表示的,所有的进程共享这张表。包括:当前的文件位置、引用计数、以及一个指向v-node表中对应表项的指针。
-
v-node表:每个表项包含stat结构中的大多数信息,;包括stmode和stsize成员,所有进程共享。
参考资料
1.《深入理解计算机系统》教材
2.小组问题:第十章关于csapp.h的提问 http://group.cnblogs.com/topic/73278.html
实践项目
首先把老师的压缩包在虚拟机中解压缩,运行每个代码
1、cp的作用就是读取一个文件的内容到存储器,在新的地址创建空白文件,再从存储器将内容写入新文件。(如下图把ls1.c复制,存为lc.c文件)
2、ls1程序的作用是在当前目录显示文件名(未带参数直接显示,带参数的存入argc)
3、ls2不仅显示了文件名,还用来显示文件的详细信息,比如用户名、群组名、大小、创建时间、读写权限等。
4、echostate代码用来检查命令行中的提示符是否显示的,如果显示,输入的命令都可见,不显示则表示输入的命令不可见。
5、fileinfo代码这个功能用来实现显示文件信息,建立了一个stat数据结构。先判断命令是否有操作数,有的话才能继续进行下去,如果没有报错就打印出来相关文件信息,报错就用perror将报错信息打印出来。
6、filesize代码用st_size成员来计算文件的字节数大小,gcc后有一个警告,调用正常。
7、setecho代码用来改变echo状态。
8、spwd代码的功能是列出当前目录。
9、testioctl代码用来显示当前文件的大小
习题筛选:
10.1
1、首先,建立相关文件10-1.c、foo.txt、baz.txt
2、将娄老师开学时上传的CSAPP.2nd.code文件解压至虚拟机中,从中找到libcsapp32.a静态库文件、csapp.h、csapp.c,把它们放在10-1.c所在的Terminal的主文件夹中。因为虚拟机为32位,.c文件中才是定义的那些函数的具体实现。
3、接下来,把csapp.c生成静态库libcsapp.a,它相当于main函数,这样可以直接使用,运行习题代码。
4、因为csapp.c中有关于线程的头文件,所以需要加上-lpthread,这个命令是使其连接csapp.h来调用csapp.c,来运行代码10-1.c,得出结果fd2 = 3。