20145233 《信息安全系统设计基础》第九周学习总结
20145233 《信息安全系统设计基础》第九周学习总结
教材学习内容总结
第一节 Unix I/O
这一节涉及到操作系统的基本抽象之一——文件。也就是说,所有的I/O设备都被模型化为文件,而所有的输入输出都被当做对相应文件的读/写。相关的执行动作如下:
打开文件
-
应用程序向内核发出请求→要求内核打开相应的文件→内核返回文件描述符
-
文件描述符:一个小的非负整数,用来在后续对此文件的所有操作中标识这个文件。有三个已经被指定了的如下:
标准输入——0(STDIN_FILENO)
标准输出——1(STDOUT_FILENO)
标准错误——2(STDERR_FILENO)
括号中是常量表示形式,使用时需要加头文件<unistd.h>
改变当前的文件位置
-
通常,读,写操作都从当前文件偏移量处开始(也就是文件位置),并使偏移量增加所读写的字节数,可以理解为光标所在的位置。
-
当打开一个文件的最初时候文件的偏移量为0.
-
通过seek操作,可以显示的设置文件的当前位置为k。
读写文件
(1)读
读操作就是从文件拷贝n>0个字节到存储器,并且改变文件当前位置。(如果当前位置是k,则改变为k+n)
EOF的来源:
- 这里有一个理解上的误区:文件结尾处没有明确的EOF信号,是当文件当前位置的数值超过了文件大小时,会处罚一个称为end-of-file的条件,能够被应用程序检测到,这就是所谓的EOF信号。
(2)写
- 写操作是从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置,然后更新当前文件位置。
关闭文件
- 应用通知内核关闭文件→内核释放文件打开时的数据结构→恢复描述符→释放存储器资源。
第二节 打开和关闭文件
- 打开一个已存在的文件或者创建一个新文件:int open(char *filename,int flags,mode_t mode) (若成功则返回新文件描述符,若出错为-1)
- open函数将filename转换为一个文件描述符,并且返回描述符数字。
- flags参数指明了进程如何访问文件。
- mode参数指定了新文件的访问权限位。
- 关闭一个打开的文件:int close(int fd)(若成功则为0,若出错则为-1)
关闭一个已关闭的描述符会出错
第三节 读和写文件
- read函数从描述符为fd的当前文件位置拷贝最多n个字节到存储器位置buf,返回值-1表示一个错误。而返回值0表示EOF。否则,返回值表示的是实际传送的字节数量。
- write函数从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置。
- 在某些情况下,read和write传送的字节比应用程序要求的要少原因如下:
1、读时遇到EOF。假设我们猪呢比读一个文件,该文件从当前文件位置开始只含有20多个字节,而我们以50个字节的片进行读取。这样一来,下一个read返回的不足值为20,此后的read将通过返回不足值0来发出EOF信号。
2、从终端读文本行。如果打开文件是与终端相关联的(如键盘和显示器),那么每个read函数将以此传送一个文本行,返回的不足值等于文本行的大小。
3、读和写网络套接字。如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会引起read和write返回不足值。对Unix管道调用read和write时,也有可能出现不足值,这种进程间的通信机制不在我们讨论的范围之内。
不足值
- 读时遇到EOF:文件末尾剩余的字节数不足读取文件的字节片大小。
- 从终端读文本行:若打开文件与终端相关联,则每个read函数将一次传送一个问本行。返回的不足值等于文本行的大小。
- 读和写网络套接字(socket):若打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会引起read和write返回不足值。
第四节 用RIO包健壮地读写
- RIO包会自动处理不足值。
- RIO提供了两类不同的函数:
- 无缓冲的输入输出函数。这些函数直接在存储器和文件之间传送数据,没有应用级缓冲,对将二进制数据读写到网络和从网络读写二进制数据尤其有用。
- 带缓冲的输入函数。这些函数允许高效地从文件中读取文本行和二进制数据(函数从内部缓冲区中拷贝一个文本行,当缓冲区变空的时候,会自动地调用read重新填满缓冲区),这些文件的内容缓存在应用级缓冲区内,类似于像printf这样的标准I/O函数提供的缓冲区。带缓冲的RIO输入函数是线程安全的,它在同一个描述符上可以被交错地调用。
第五节 读取文件数据
- 元数据即文件信息,需要用到的函数是stat和fstat
- stat需要输入文件名,而fstat需要输入的是文件描述符。
- 文件类型:
普通文件:二进制或文本数据,宏指令:S_ISREG()
目录文件:包含其他文件的信息,宏指令:S_ISDIR()
套接字:通过网络和其他进程通信的文件,宏指令:S_ISSOCK()
- Unix提供的宏指令根据st_mode成员来确定文件的类型
第六节 共享文件
- 关于文件共享的几个相关图片,以及每种方式的流程,在课本607页。
- 内核用三个相关的数据结构来表示其打开的文件。
- 描述符表:表项由进程打开的文件描述符来索引的,每个打开的描述符表指向文件表中的一个表项,每个进程有其独立的描述符表。
- 文件表:打开文件的集合是由一张文件表来表示的,所有的进程共享这张表。包括:当前的文件位置、引用计数、以及一个指向v-node表中对应表项的指针。
- v-node表:每个表项包含stat结构中的大多数信息,;包括st_mode和st_size成员,所有进程共享。
- 多个描述符可以通过不同的文件表表项来引用同一个文件。 关键思想是每个描述符都有它自己的文件位置,所以对不同描述符的读操作可以从文件的不同位置获取数据。
- 在内核删除相应文件表项之前,父子进程必须都关闭了它们的描述符。
第七节 I/O重定向
- I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来。unix> ls > foo.txt
- I/O重定向是依靠dup2函数工作的。dup2函数拷贝描述符表表项oldfd到描述符表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开,dup2会在拷贝oldfd之前关闭newfd。
#include <unistd.h>
int dup2(int oldfd,int newfd);
第八节 标准I/O
- 标准I/O库将一个打开的文件模型化为一个流,也就是一个指向FILE类型的结构的指针。
#include <stdio.h>
extern FILE *stdin; /*标准输入,文件描述符为0*/
extern FILE *stdout; /*标准输出,文件描述符为1*/
extern FILE *stderr; /*标准错误,文件描述符为2*/
类型为file的流是对文件描述符和流缓冲区的抽象,目的是使开销较高的Unix I/O系统调用的数量尽可能小。
本周代码托管链接
本周的遇到的问题
-
在练习10.1的代码中,运行的话是没办法直接编译这个代码的,因为没有这个“csapp.h”这个头文件,所以需要下载这个头文件,在网上查阅了相关的资料,我在最后贴出解决方案的链接。
-
通过上网查找发现csapp.h文件其实是作者自己弄的一个头文件,Linux中并不包含这个。网上说在教材配套网站http://csapp.cs.cmu.edu/public/code.html上可以下载。
-
但是这个网站打不开……最后,我在CSDN.NET上找到了csapp.h和csapp.c,按照网上说的在csapp.h文件中#endif前加上了#include"csapp.c",并放到了/usr/lib文件夹中,并用gcc xxx.c -o xxx -lpthread语句通过了编译(因为代码中包含多线程)。
-
其中还有一点很重要就是open和close需要小写。
-
最后结合我在网上找到的内容,最终成功运行。
-
第一个是没有文本的结果:
-
第二个是有文本的结果:
其他(感悟、思考等,可选)
- 本章的内容其实不是很多,书上只有十几页的内容,但是内容其实比较多需要理解其中的具体意思,在这里我觉得老师多次提到的常翻阅课本的好处,第一次看书的时候,我对其中的一部分还是不理解,但是在第二次再次看的时候,就明显觉得好理解很多,以练习题10.1为例子,这个题目就很好的锻炼了我们的能力,需要理解代码不说,也看到了每个问题的所在,需要自己一步步解决。
- 并且网上可以找到很多有用的方法,有些很繁琐,而有些很简便,这都是自己在学习时候的收获。
练习题
10.1
- 已在上面解决
10.2
- fd1和fd2有独立的文件描述符,所以是典型的没有共享的打开文件方式
- 它们各自有各自的描述符表、文件表、v-code表,每个描述符对于foobar.txt都有它自己的文件位置,所以它们的读取是各自独立的,
- 从fd2的读操作会读取foobar.txt的第一个字节,因此最后得值是f,输出“c = f”
10.3
- 子进程会继承父进程的描述符表,以及所有进程共享的同一个打开文件表。
- 因此当子进程读取文件的第一个字节时,文件位置加一,所以父进程会读取第二个字节,输出c=o
10.4
- 要使重定向标准输入(描述符0)到描述符5,我们可以调用dup2(5,0)或者等价的dup(5,STDIN_FILENO)
10.5
- 因为重定向的关系fd1重定向到fd2,输出c=o
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 24篇 | 350小时 | |
第一周 | 0/0行 | 1/2 | 20小时 | |
第二周 | 53/53行 | 1/3 | 25/45小时 | |
第三周 | 130/183行 | 1/4 | 30/75小时 | |
第四周 | 0/183行 | 0/4 | 5/80小时 | |
第五周 | 158/341行 | 1/5 | 30/110小时 | |
第六周 | 84/425行 | 2/7 | 30/140小时 | |
第七周 | 209/634行 | 1/7 | 30/170小时 | |
第八周 | 0/634行 | 2/9 | 25/195小时 | |
第九周 | 83/717行 | 2/11 | 30/225小时 |