• 20145229 《信息安全系统设计基础》第9周学习总结


    20145229《信息安全系统设计基础》第9周学习总结

    教材学习内容总结

    系统级I/O

    • 输入/输出是在主存和外部设备之间拷贝数据的过程
    • 所有语言的运行时系统都提供执行I/O的较高级别的工具,高级别I/O函数可以满足大多数情况,但是有时候除了使用Unix I/O别无选择
    • I/O是系统操作不可或缺的一部分,要理解I/O,必须先理解进程

    10.1 Unix I/0

    • Unix I/O:一种将所有的I/O设备模型化为文件,将所有输入输出当作文件的读和写来执行,允许内核引出一个应用接口的方式
      1.打开文件
      应用程序要求内核打开文件表示它想访问一个I/O设备,内核返回一个描述符(非负整数)作为回应。描述符在之后的操作中标识这个文件,应用程序只需要记住标识符
      每个进程开始打开的三个文件:标准输入(描述符为0)、标准输出(描述符为1)、标准错误(描述符为2).头文件定义的常量为:STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。它们可以代替描述符

    2.改变当前文件的位置

    • 每一个打开的文件,都有一个文件位置K,初始为0,代表了文件起始地字节偏移量。应用程序可执行seek设置文件当前位置为k

    3.读写文件

    • 读文件就是从文件拷贝n个字节到存储器,从k到k+n。给一个m字节的文件,k>m时执行读会触发EFO,应用程序可以检测到,但是结尾不会标记出来
    • 写文件就是从存储器拷贝n个字节到文件,从k开始,然后更新K

    4.关闭文件

    • 当应用完成后会通知内核关闭文件,内核会释放文件打开时的数据结构并将描述符放回可用状态。不管如何终止进程,内核都会关闭所有文件并释放存储器资源

    10.2 打开和关闭文件

    • 进程是通过调用open函数打开一个已存在的文件或者创建一个新文件

    • open函数将filename转换为一个文件描述符,并且返回描述符数字,返回的描述符数字是进程中没有打开的最小描述符。

    • flags参数:O_RDONLY:只读
      O_WRONLY:只写
      O_RDWR:可读可写

    • flags参数:0_CREAT:如果文件不存在,就创造它的截断的空文件
      0_trunc:如果文件存在,截断
      0_APPEND:每次操作之前,设置文件位置到文件结尾处

    • mode参数指定了文件的访问权限位,每个文件都有一个umask,通过调用umask设置

    • 当文件通过带有umask的open函数创建文件,文件的访问权限为 mode &~umask

    10.3 读与写文件

    • 应用程序通过分别调用read和write函数来执行输入和输出
    • ssize_t read(int fd, void *buf, size_t n);//0 EOF,-1 错误,n实际传送的字节数。
    • ssize_t write(int fd, const void *buf, size_t n);// size_t是unsigned ssize_t有符号
    • read进行读取的时候,返回-1代表错误,返回0代表EOF。否则返回的值代表实际传送的字节数量
    • write进行输出的时候,通过调用lseek函数来显式的修改文件当前的位置
    • 当read和write传送的字节比要求少时,这些不足值不表示有错误:
      1.读取时遇到EOF,此后的read将用过返回值为0来发出EOF信号
      2.从终端读文本行,一次传送一个文本行,不足值为文本行的大小
      3.读和写网络套接字,内部缓冲约束和网络延迟会导致返回不足值;Unix调用read和write时也会返回不足值
    • 实际上除了EOF都会遇到不足值。若需要强健的网络应用则需要反复调用read和write

    ssize_t和size_t的区别

    size_t是一个无符号int,ssize_t是一个有符号int,因为错误的时候必须返回-1,-1是一个有符号数,所以read函数返回必须用ssize_t,且这样的返回会使read的最大值从4GB减到2GB

    10.4 用RIO健壮的读写

    • RIO包将会处理不足值
    • RIO包:
      无缓冲的输入输出函数在:直接在存储器和文件中传送数据(二进制数据写到网络和从网络中读写二进制)
      带缓冲的输入函数:高效的从文件中读取文本行和二进制数据,文件内容可缓存在应用级缓冲区
    • 带有缓冲的RIO输入函数是线程安全的,在同一个描述符上可以被交替的调用

    10.4.1 RIO无缓冲的输入输出函数

    • 通过调用rio_readn和rio_writen函数,直接在存储器和文件中传送数据
    • rio_readn遇到EOF时只能返回一个不足值,rio_writen不会返回不足值。同一个描述符可以任意交错调用rio_readn和rio_writen
    • 若函数被程序的返回中断,函数会手动重启,我们允许被中断的系统调用,在必要的时候重启他们

    10.4.2 RIO带缓冲的输入函数

    • 一个文本行是一个由换行符结尾的ASCII码序列,换行符’ ‘与LF相同,数字值为0x0a
    • 包装函数(rio_readlineb)从读缓冲区中读取文件,缓冲区空的时候自动调用read重新填满
    • rio_readlineb将描述符和地址处的rio_t读缓冲区联系起来,rio_readlineb每次最多读maxlen-1,留一个给结尾的空字符,超过的被截断
    • rio_readnb最多读n个字节。同一个描述符对rio_readlineb和rio_readnb可以交叉使用
    • RIO读程序的核心是rio_read函数,rio_read是Unix read带缓冲区的版本,若缓冲区为空则read调用会收到不足值且不是错误。缓冲区非空的话,从缓冲区中拷贝并返回拷贝字节数
    • 对于一个应用程序,rio_read和Unix read有同样的语意。出错时返回-1,并设置errno。EOF时返回0.若字节超过缓冲区未读字节,返回不足值。

    10.5 读取文件元数据

    • 应用程序通过调用stat和fstat来检索文件信息(元数据)
    • stat以一个文件名作为输入,fstat用文件描述符作为输入
    • st_size表示文件的大小,st_mode表示编译的文件许可位和文件类型。Unix识别文件类型。普通文件包括文本数据和二进制文件。目录文件包含其他文件的信息。套接字是用来通过网络和其他进程的文件
    • Unix提供的宏指令根据st_mode确定文件的类型

    10.6 共享文件

    三种数据结构表示打开的文件:

    • 描述符表。每个进程都有独立的描述符表

    • 文件表。所有进程共享一个文件表。文件表由当前文件位置、引用计数、指向v-node表中对应的指针。内核不会删除表项,除非引用计数为0

    • v-node表:所有进程共享v-node表,表项包含stat的大多数信息

    • 描述符各自引用不同的文件,没有共享文件

    • 多个描述符通过不同的文件表表项引用同一个文件

    • 子进程继承父进程打开文件

    10.7 I/O重定向

    • Unix外壳提供了I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来
    • 重定向工作方式:使用dup2函数
      include<unistd.h>
      int dup2(int oldfd,int newfd);
    • dup2函数拷贝描述符表表项oldfd到描述符表表项newfd,覆盖描述表表项newfd以前的内容。若newfd已经打开,dup2会在拷贝oldfd之前关闭newfd。

    练习题

    10.1
    include "csapp.h"
    int main()
    {
    int fd1,fd2;
    fd1=Open("foo.txt",O_RDONLY,0);
    Close(fd1);
    fd2=Open("baz.txt",O_RDONLY,0);
    printf("fd2=%d ",fd2);
    exit(0);
    }

    unix进程生命周期开始的时候,就已经有了三个打开的描述符:标准输入、标准输出、标准错误,分别为0,1,2。所以fd2的值应该是3

    10.3就像前面那样,磁盘文件foobar.txt由6个ASCII码字符“foobar”组成。那么,下列程序的输出是什么?
    include "csapp.h"
    int main()
    {
    int fd;
    char c;
    fd=Open("foobar.txt",O_RDONLY,0);
    if(Fork()==0)
    {
    Read(fd,&c,1);
    exit(0);
    }
    Wait(NULL);
    Read(fd,&c,1);
    printf("c=%c ",c);
    exit(0);
    }

    父子进程共享相同的文件表表项,因此依次读取的是“f”和“o”。输出为o。

    10.4 如何使用dup2将标准输入重定向到描述符5?

    重定向标准输入(描述符0)到描述符5,可以调用dup2(5,0)进行重定向。

    10.5假设磁盘文件foobar.txt由6个ASCII码字符“foobar”组成。那么,下列程序的输出是什么?
    include "csapp.h"
    int main()
    {
    int fd1,fd2;
    char c;
    fd1=Open("foobar.txt",O_RDONLY,0);
    fd2=Open("foobar.txt",O_RDONLY,0);
    Read(fd1,&c,1);
    Read(fd2,&c,1);
    printf("c=%c ",c);
    exit(0);
    }

    描述符fd1和fd2都有各自的打开文件表表项,所以有它们各自的文件位置(也就是说互不影响,不会因为fd1先执行就使得fd2打开的文件位置推后)。则fd2打开文件读出的第一个字母还是f。

    代码调试中的问题和解决过程

    运行10.1的时候出现了错误

    参考了论坛里卢肖明同学和高其同学解决的途径解决了
    缺少csapp.h的头文件,这是书的作者编写的一个头文件,使用的时候要把此头文件csapp.h和csapp.c文件包含到你的系统中。先到网上下载这两个文件,下载地址(http://download.csdn.net/detail/tzasd89812/4206284);
    在命令行下输入sudo mv csapp.h csapp.c /usr/include指令将文件移到/usr/include中;打开csapp.h头文件,在#end if前面加上一句#include <csapp.c>

    本周代码托管截图

    其他(感悟、思考等,可选)

    这周的学习内容跟以前相比少了一点,本周学习了系统的输入、输出,了解了一些I/O函数,相较于之前几周感觉轻松不少。但是,页数少并不代表不重视,认真去理解系统的输入、输出对于我们理解程序的运行至关重要,只有掌握了其中的原理我们才能在实践中更加有意义。

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 130/130 1/1 17/17
    第二周 90/270 1/1 16/16
    第三周 120/390 2/2 16/16
    第四周 89/479 1/1 17/17
    第五周 120/599 1/1 16/16
    第六周 110/709 1/1 18/18
    第七周 128/837 1/1 18/18
    第八周 5/842 1/1 16/16
    第九周 65/907 1/1 13/13

    参考资料

  • 相关阅读:
    牛客练习赛24 E:青蛙(最短路)
    菜根谭#10
    菜根谭#9
    菜根谭#8
    菜根谭#7
    菜根谭#6
    菜根谭#5
    菜根谭#4
    菜根谭#3
    菜根谭#2
  • 原文地址:https://www.cnblogs.com/20145229ss/p/6056803.html
Copyright © 2020-2023  润新知