zoukankan      html  css  js  c++  java
  • 20145203 《信息安全系统设计基础》第九周学习总结

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

    第十章 系统级I/O

    教材学习内容总结

    绪论

    输入/输出(I/O)是在主存外部设备之间拷贝数据的过程。

       输入:从I/O设备拷贝数据到主存
       输出:从I/O设备拷贝数据到主存

    第一节 Unix I/O

    Unix I/O:所有的I/O设备都被模型化为文件,而所有的输入输出都被当做对相应文件的读/写。执行方式如下:

    1.打开文件:

    应用程序向内核发出请求→要求内核打开相应的文件→内核返回文件描述符

    • 文件描述符:一个小的非负整数,用来在后续对此文件的所有操作中标识这个文件。有三个已经被指定了的描述符如下:

        标准输入——0(STDIN_FILENO)
        标准输出——1(STDOUT_FILENO)
        标准错误——2(STDERR_FILENO)
      

    括号中是常量表示形式,使用时需要加头文件<unistd.h>

    2.改变当前的文件位置:

    当打开一个文件的最初时候文件的偏移量为0.通过seek操作,可以显示的设置文件的当前位置为k。

    3.读写文件

    (1)读:

    ①读操作就是从文件拷贝n>0个字节到存储器,并且改变文件当前位置。(如果当前位置是k,则改变为k+n)

    文件结尾处没有明确的EOF信号,是当文件当前位置的数值超过了文件大小时,会处罚一个称为end-of-file的条件,能够被应用程序检测到,这就是所谓的EOF信号。

    (2)写:

    写操作是从存储器拷贝n>0个字节到一个文件,然后更新当前文件位置。

    4.关闭文件

    应用通知内核关闭文件→内核释放文件打开时的数据结构→恢复描述符→释放存储器资源。

    第二节 打开和关闭文件

    1.open函数

    (1)函数定义:

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    int open(char *filename, int flags, mode_t mode);
    

    (2)参数解析:

    • 返回值:类型为int型,返回的是描述符数字,总是在进程中当前没有打开的最小描述符。如果出错,返回值为-1.

    • filename:文件名

    • flags:指明进程打算如何访问这个文件,可以取的值见下:

        O_RDONLY:只读
        O_WRONLY:只写
        O_RDWR:可读可写
      
        O_CREAT:文件不存在,就创建新文件
        O_TRUNC:如果文件存在,就截断它
        O_APPEND:写操作前设置文件位置到结尾处
      

    这些值可以用或(|)连接起来。

    • mode:指定了新文件的访问权限位,符号名称如下:

    当进程通过带某个mode参数的open函数调用来创建一个新文件时,文件的访问权限位被设置为mode & ~umask

    2.close函数

    (1)函数定义:

    #include <unistd.h>
    
    int close(int fd);
    

    (2)参数解析:

    • 返回值:成功返回0,出错返回-1
    • fd:即文件的描述符。

    第三节 读和写文‘件

    1.读 read

    (1)函数原型:

    #include <unistd.h>
    
    ssize_t read(int fd, void *buf, size_t n);
    

    (2)参数解析:

    • 返回值(ssize_t):成功则返回读的字节数,EOF返回0,出错返回-1。返回值为有符号数。
    • fd:文件描述符
    • n:最多从当前文件位置拷贝n个字节到存储器位置buf

    2.写 write

    (1)函数原型:

    #include <unistd.h>
    
    ssize_t write(int fd, void *buf, size_t n);
    

    (2)参数解析:

    • 返回值(ssize_t):成功则返回写的字节数,出错返回-1。返回值为有符号数。
    • fd:文件描述符
    • buf:存储器位置
    • n:最多从存储器位置buf拷贝n个字节到当前文件位置

    注意:

    ①read和write在正常情况下返回值是实际传送的字节数量。

    ②通过lseek函数可以显式的修改当前文件的位置

    ③不足值:

    不足值指在某些情况下,read和write传送的字节比应用程序要求的要少,原因如下:

    • 读的时候遇到EOF
    • 从终端读文本行
    • 读和写socket
    • 对Unix管道调用 read和write

    第四节 用RIO包健壮的读写

    RIO,Robust I/O,自动的处理上文所述的不足值。

    1.RIO的无缓冲的输入输出函数。

    这些函数的作用直接在存储器和文件之间传送数据,常适用于网络和二进制数据之间。

    (1)rio_readn函数和rio_writen定义:

    #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);
    

    (2)参数解析:

    • fd:文件描述符

    • usrbuf:存储器位置

    • n:传送的字节数

    • 返回值:

        rio_readn成功则返回传送的字节数,EOF为0(一个不足值),出错为-1
        rio_writen成功则返回传送的字节数,出错为-1,没有不足值。
      

    2.RIO的带缓冲的输入函数

    可以高效的从文件中读取文本行和二进制数据。

    一个文本行就是一个由换行符结尾的ASCII码字符序列。所以统计文本文件中文本行的数量只需要通过计算换行符。需要用到的函数:

    #include "csapp.h"
    
    void rio_readinitb(rio_t *rp, int fd);//将描述符fd和地址rp处的一个类型为rio_t的读缓存区联系起来。
    
    ssize_t rio_readlineb(rio_t *rp,void *usrbuf, size_t maxlen);//从文件rp中读出一个文本行,包括换行符,拷贝到存储器位置usrbuf,并用空字符结束这个文本行。最多赌到maxlen-1个字节,最后一个给结尾的空字符。
    ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);//从文件rp中读取最多n个字符到存储器位置usrbuf中。
    
    成功则返回传送的字节数,EOF为0,出错为-1。
    

    第五节 读取文件元数据

    1、元数据即文件信息,需要用到的函数是stat和fstat。

    (1)定义如下:

    #include <unistd.h>
    #include <sys/stat.h>
    
    int stat(const char *filename, struct stat *buf);
    int fstat(int fd,struct stat *buf);
    

    (2)参数:

    • 返回值:成功为0,错误为-1

    • stat需要输入文件名,而fstat需要输入的是文件描述符。

    • 关于stat数据结构如下图:

    需要注意的有两个,st_mode和st_size。

       st_size:包含文件的字节数大小
       st_mode:包编码文件访问许可位和文件类型。
       
    Unix文件类型如下,并有对应的宏指令,含义均为“是xx吗”,这些宏在sys/stat.h中定义:

    	普通文件	二进制或文本文件(对内核没差)	S_ISREG()
    	目录文件	关于其他文件的信息	           S_ISDIR()
    	套接字	通过网络与其他进程通信的文件	S_ISSOCK()
    

    (3)查询和处理一个文件的st_mode位:

    #include "csapp.h"
    
    int main (int argc, char **argv) 
    {
        struct stat stat;
        char *type, *readok;
    
        Stat(argv[1], &stat);//文件选择argv[1],写入一个stat数据结构
        if (S_ISREG(stat.st_mode))     /* 如果是一个文本文件 */
    	type = "regular";
        else if (S_ISDIR(stat.st_mode))//如果是一个目录文件
    	type = "directory";
        else 
    	type = "other";
        if ((stat.st_mode & S_IRUSR)) /* 检查阅读权限 */
    	readok = "yes";
        else
    	readok = "no";
    
        printf("type: %s, read: %s
    ", type, readok);
        exit(0);
    }
    

    第六节 共享文件

    示例:

    1、典型的无共享的:

    描述符1和4指向文件表中不同的表现,进而引用了两个不同的文件。

    2、文件共享:

    这里可以看到,描述符1和4指向了文件表中的不同表项,但是引用了同一个文件,关于这种情况书上给了一个实例:同一个filename调用open函数两次,这时描述符是不一样的,文件位置也不一样,但是都是同一个文件。这体现的关键思想是:

    每个描述符都有它自己的文件位置 ,所以对不同描述符的读操作可以从文件的不同位置获取数据。

    3、子进程继承父进程的打开文件:

    初始状态如图6,只有父进程进行了打开文件,然后子进程会有一个父进程描述符表的副本,因而能够共享相同的打开文件表集合,同时也就共享相同的文件位置。

    而由于文件表的性质,关闭一个描述符的时候只会减少相应的文件表表项中的引用计数,内核不会删除这个文件表表项直至引用计数清零,所以要想内核删除相应文件表表项,父子进程都必须关闭它们的描述符。

    第七节 I/O重定向

    I/O重定向操作符:

    Unix>ls > foo.txt
    这句代码的含义就是使外壳加载和执行ls程序,并且将标准输出重定向到磁盘文件foo.txt。

    I/O重定向函数: dup2

    (1)函数定义为:

    #include <unistd.h>
    
    int dup2(int oldfd, int newfd);
    

    (2)返回值:成功返回描述符,错误返回-1

     函数将拷贝描述符表表项oldfd,覆盖描述表表项newfd,如果后者被打开,则在拷贝前关闭它。
    

    第八节 标准I/O

    1.标准I/O库:

    ANSI C定义了一组高级输入输出函数,称为标准I/O库,包含:

    • fopen、fclose,打开和关闭文件
    • fread、fwrite,读和写字节
    • fgets、fputs,读和写字符串
    • scanf、printf,复杂的格式化的I/O函数

    2.流——类型为FILE的流是对文件描述符和流缓冲区的抽象

    标准I/O库将一个打开的文件模型化为一个流。

    每个ANSI C程序开始的时候都有三个打开的流:stdin、stdout、stderr,对应于标准输入、标准输出和标准错误 (参见第一节笔记),定义如下:

    #include <stdio.h>
    
    extern FILE *stdin;
    extern FILE *stdout;
    extern FILE *stderr;
    

    第九节 套接字

    网络套接字上最好不要使用标准I/O函数来进行输入和输出,而是使用RIO函数,原因时标准I/O函数会有限制:

    ①如果没有清楚缓存区,输入函数后面不能接输出函数。

    ②输出函数后面也不能接输入函数,导致对套接字使用lseek是非法的,打开两个流很容易出现灾难。

    课后作业中的问题和解决过程

    1、练习10.1:在Unix生命周期一开始,0、1、2就被占用,以后的open只能从3开始

    2、练习10.2:因为fd1和fd2有独立的文件描述符,它们各自有各自的描述符表、文件表、v-code表,所以它们的读取是各自独立的,最后得值是f;

    3、练习10.3:Fork是子程序,和父程序共享同一个描述符表、文件表、v-code表,指向相同的文件,所以在子程序执行过后,父程序在其基础上进行,读取下一个字符,是o

    4、练习10.5:初始情况下fd1和fd2的描述符分别是3和4,所以是两个不同描述符表,指向两个不同的文件,但是由于在读了fd2一个字节之后,将fd1重定向到了fd2,所以此时再读fd1相当于在读fd2,也就是结果是o。

    本周代码托管链接

    代码链接

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

    这一章教材上的内容不算多,但是非常的抽象,我花了比想象中更多的时间来通读书上的代码,又参考了很多人对于文件共享的解释,才理解了文件共享的表层含义。读书的过程中涉及到很多专业名词,我都不是很理解,只能根据上下文来猜测或者去百度,去试图解释每一句的含义,学的还是比较吃力。但是这次学到了非常实用的东西——文件的读写。不光学习这门课,对于我学习操作系统以及编写C语言代码都是非常有帮助的。希望自己能学以致用。

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 150/150 1/2 20/20
    第二周 200/350 1/2 24/44
    第三周 150/500 1/3 20/64
    第五周 300/800 1/4 15/79
    第六周 500/1300 1/5 20/99
    第七周 200/1500 1/6 21/120
    第九周 210/1710 1/9 10/130

    参考资料

  • 相关阅读:
    bzoj1415 NOI2005聪聪和可可
    Tyvj1952 Easy
    poj2096 Collecting Bugs
    COGS 1489玩纸牌
    COGS1487 麻球繁衍
    cf 261B.Maxim and Restaurant
    cf 223B.Two Strings
    cf 609E.Minimum spanning tree for each edge
    cf 187B.AlgoRace
    cf 760B.Frodo and pillows
  • 原文地址:https://www.cnblogs.com/GZSdeboke/p/6060171.html
Copyright © 2011-2022 走看看