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

    参考资料

  • 相关阅读:
    HDU 3848 CC On The Tree 树形DP
    编程求取直线一般式表达式,两直线交点
    向外国学者所要论文源代码--英语模版
    找出该树中第二小的值--思路及算法实现
    不使用额外空间交换2个数据的源代码
    华为2018软件岗笔试题解题思路和源代码分享
    华为笔试题--LISP括号匹配 解析及源码实现
    Linux 快捷键汇总(偏基础)
    快速排序算法思路分析和C++源代码(递归和非递归)
    Python读取SQLite文件数据
  • 原文地址:https://www.cnblogs.com/GZSdeboke/p/6060171.html
Copyright © 2011-2022 走看看