信息安全系统设计基础第九周学习总结
第十章 系统级I/O
内容总结:
一、每个unix文件都是一个m字节的序列;所有I/O设备,如网络、磁盘和终端都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行。
二、输入/输出(I/O)是在主存和外部设备之间拷贝数据的过程。输入操作是从I/O设备拷贝数据到主存,输出操作是从主存拷贝数据到I/O设备。
三、 I/O重定向:
1、Unix外壳提供了I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来。
unix > ls > foo.txt
使外壳加载和执行ls程序,将标准输出重定向到磁盘文件foo.txt。
2、I/O重定向函数: dup2
函数定义为:
#include <unistd.h>
int dup2(int oldfd, int newfd);
返回:若成功则为非负的描述符,若出错则为-1。
dup2函数拷贝描述符表表项oldfd到描述符表表项newfd,覆盖描述符表表项newfd以前的内容,如果newfd被打开了,dup2会在拷贝oldfd之前关闭newfd。
四、标准I/O
1、ANSI C定义了一组高级输入输出函数,称为标准I/O库,包含:
(1)fopen、fclose,打开和关闭文件
(2)fread、fwrite,读和写字节
(3)fgets、fputs,读和写字符串
(4)scanf、printf,复杂的格式化的I/O函数
2、标准I/O库将一个打开的文件模型化为一个流。对于程序员而言,一个流就是一个指向FILE类型的结构的指针。
3、每个ANSI C程序开始的时候都有三个打开的流:stdin、stdout、stderr,分别对应于标准输入、标准输出和标准错误:
#include <stdio.h>
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
类型为FILE的流是对文件描述符和流缓冲区的抽象。流缓冲区的目的和RIO读缓冲区的一样:就是使开销较高的Unix I/O系统调用的数量尽可能的小。
五、Unix I/O是在操作系统内核中实现的。应用程序可以通过open、close、lseek、read、write和stat这样的函数来访问Unix I/O。
六、标准I/O流,从某种意义上而言是全双工的,因为程序能够在同一个流上执行输入和输出。然而 ,对流的限制和对套接字的限制,有时候会互相冲突,而又极少有文档描述这些现象:
限制一:跟在输出函数之后的输入函数。
限制二:跟在输入函数之后的输出函数。
七、在网络套接字上不要使用标准I/O函数来进行输入和输出,而是使用健壮的RIO函数。
课后练习题:
练习题1
下面的程序输出是什么?
#include "csapp.h"
int main()
{
int fd1, fd2;
fd1 = Open("foo.txt", O_RDONLY, O);
Close(fd1);
fd2 = Open("baz.txt", O_RDONLY, O);
printf("fd2 = %d\n", fd2);
exit(0);
}
答案:Unix进程生命周期开始时,打开的描述符赋给了stdin(描述符0)、stderr(描述度2)。open函数总是返回最低的未打开的描述符,所以第一次调用open会返回描述符3.调用close函数会释放描述符3,最后对open的调用会返回描述符3,因此程序的输出是”fd2=3“.
练习题2
假设磁盘文件foobar.txt由6个ASCII码字符“foobar”组成。那么下列程序的输出是什么? 程序如下:
#include "csapp.h"
int main()
{
int fd1, fd2;
char c;
fd1 = Open("foobar.txt", O_RDONLY, O);
fd2 = Open("foobar.txt", O_RDONLY, O);
Read(fd, &c, 1);
Read(fd2, &c, 1);
printf("c = %c\n", c);
exit(0);
}
答案: 描述符fd1和fd2都有各自的打开文件表表项,所以每个描述符对于foobar.txt都有它自己的文件位置。因此,从fd2的读操作会读取,foobar.txt的第一个字母,并输出 c = f
练习题3
就像前面一样,假设磁盘文件foobar.txt由6个ASCII码字符”foobar“组成。那么下列程序的输出是什么?
#include "csapp.h"
int main()
{
int fd;
char c;
fd = Open("foobar.txt", O_RDONLY, O);
if(Fork()==0){
Read(fd, &c, 1);
exit(0);
}
Wait(NULL);
Read(fd, &c, 1);
printf("c = %c\n", c);
exit(0);
}
答案:因为子进程会继承父进程的描述符表,以及所有进程共享的同一个打开文件表。因此,描述符fd在父子进程中都指向同一个文件表表项。当子进程读取文件的第一个字母时,文件位置加1.因此,父进程会读取第二个字节,而输出就是c = 0
练习题4
如何用dup2将标准输入重定向到描述符5?
答案: 重定向标准输入(描述符0)到描述符5,我们将调用dup2(5,0)或者等价的dup2(5,STDIN_FILENO).
练习题5
假设磁盘文件foobar.txt由6个ASCII码字符“foobar”组成。那么下列程序的输出是什么?
#include "csapp.h"
int main()
{
int fd1, fd2;
char c;
fd1 = Open("foobar.txt", O_RDONLY, O);
fd2 = Open("foobar.txt", O_RDONLY, O);
Read(fd2, &c, 1);
Dup2(fd2, fd1);
Read(fd1, &c, 1);
printf("c = %c\n", c);
exit(0);
}
答案:因为我们将fd1重定向到了fd2,输出实际上是c = 0.
参考资料
《深入理解计算机系统》
心得体会:
本周将书本第十章的内容重新看了一遍,重点放在了I/O这一块,然后认真做了课后的练习题,发现对内容的掌握更深一步。