信息安全系统设计基础第九周学习总结

第十章  系统级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 ", 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 ", 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 ", 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 ", c);

      exit(0);

}

答案:因为我们将fd1重定向到了fd2,输出实际上是c = 0.

参考资料

 《深入理解计算机系统》

心得体会:

  本周将书本第十章的内容重新看了一遍,重点放在了I/O这一块,然后认真做了课后的练习题,发现对内容的掌握更深一步。

原文地址:https://www.cnblogs.com/20135235my/p/4972643.html