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

    20145216史婧瑶《信息安全系统设计基础》第九周学习总结

    教材内容总结

    第十章 系统级I/O

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

    第一节 Unix I/O

    这一节涉及到操作系统的基本抽象之一——文件。也就是说,所有的I/O设备都被模型化为文件,而所有的输入输出都被当做对相应文件的读/写。相关的执行动作如下:

    1.打开文件:

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

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

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

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

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

    在UNIX下还有stdin,stdout,stderr表示同样的含义。
    
    二者的主要区别为:
    
    1.数据类型不同,前者为int类型,后者为FILE*;
    
    2.STDIN_FILENO主要用在read(),write()等中,后者主要用在fread(),fwrite()以f开头。
    

    2.改变当前的文件位置

    通常,读,写操作都从当前文件偏移量处开始(也就是文件位置),并使偏移量增加所读写的字节数,可以理解为光标所在的位置。

    当打开一个文件的最初时候文件的偏移量为0.

    通过seek操作,可以显示的设置文件的当前位置为k。

    3.读写文件

    (1)读

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

    ※EOF的来源:

    这里有一个一直以来的理解上的误区:文件结尾处没有明确的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:指定了新文件的访问权限位

    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)参数解析:

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

    2.写 write

    (1)函数原型:

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

    (2)参数解析:

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

    (需要注意的是,read和write在正常情况下返回值是实际传送的字节数量)

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

    4.不足值

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

    • 读的时候遇到EOF
    • 从终端读文本行
    • 读和写socket

    第四节 用RIO包健壮的读写

    RIO,Robust I/O,针对的出现不足值的问题。

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

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

    rioreadn函数和riowriten定义:

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

    参数解析:

    • fd:文件描述符
    • usrbuf:存储器位置
    • n:传送的字节数
    • 返回值:
      • rio_readn成功则返回传送的字节数,EOF为0(一个不足值),出错为-1
      • rio_writen成功则返回传送的字节数,出错为-1,没有不足值

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

    缺点:效率不是很高,每读取文件中的一个字节都要求陷入内核

    第五节 读取文件元数据

    元数据即文件信息,需要用到的函数是stat和fstat。定义如下:

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

    参数解析:

    • 返回值:成功为0,错误为-1
    • 输入:stat需要输入文件名,而fstat需要输入的是文件描述符。

    需要注意的有两个,stmode和stsize。

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

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

    第六节 共享文件

    内核用三个相关的数据结构来表示打开的文件:

    • 描述符表
    • 文件表:打开文件的集合是由一张文件表来表示的。
    • v-node表

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

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

    第七节 I/O重定向

    I/O重定向操作符: >

    ls > foo.txt
    

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

    I/O重定向函数: dup2

    函数定义为:

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

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

    这个函数执行的操作是,拷贝描述符表表项oldfd,覆盖描述表表项newfd,如果后者被打开,则在拷贝前关闭它。

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

    第八节 标准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;
    

    附录A

    错误处理

    1.错误处理风格

    (1)Unix风格

    遇到错误后返回-1,并且将全局变量errno设置为指明错误原因的错误代码;

    如果成功完成,就返回有用的结果。

    (2)Posix风格

    返回0表示成功,返回非0表示失败;

    有用的结果在传进来的函数参数中。

    (3)DNS风格

    有两个函数,gethostbyname和gethostbyaddr,失败时返回NULL指针,并设置全局变量h_errno。

    2.错误处理包装函数

    • Unix风格

    成功时返回void,返回错误时包装函数打印一条信息,然后退出。

    void Kill(pid_t pid, int signum) 
    {
      int rc;
    
      if ((rc = kill(pid, signum)) < 0)
      unix_error("Kill error");
    }
    
    • Posix风格

    成功时返回void,错误返回码中不会包含有用的结果。

    void Pthread_detach(pthread_t tid) 
    {
      int rc;
    
      if ((rc = pthread_detach(tid)) != 0)
      posix_error(rc, "Pthread_detach error");
    }
    
    • DNS风格

      struct hostent *Gethostbyname(const char *name) 
      {
        struct hostent *p;
      
        if ((p = gethostbyname(name)) == NULL)
        dns_error("Gethostbyname error");
        return p;
      }
      

    教材练习题解析

    10.1:在Unix生命周期一开始,0、1、2就被占用,以后的open只能从3开始,因此对open的调用会返回描述符3。

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

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

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

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

    问题:无法编译教材上的代码,显示如图所示错误,未解决

    代码托管截图

    链接:https://git.oschina.net/sjy519/linux-program-C/tree/master

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

    本周学习了系统级I/O的相关知识点,虽然教材中的内容不是很多,但是学起来并不容易,我感觉书上的文字讲解较抽象,因此理解起来有难度。在实践代码的过程中,又出现了问题,显示没有csapp.h这个文件或目录,自己上网百度后仍然没有解决这个问题,看到有同学发表相关小组话题,但是也没有得到解决问题的方法,希望之后能将这个问题解决,再将教材中的代码验证一遍。

    学习进度条

     代码行数(新增/累积)博客量(新增/累积)学习时间(新增/累积)重要成长
    目标 3000行 30篇 300小时  
    第一周 0/0 1/2 25/40 学习了Linux基础知识和核心命令 
    第二周 0/0 0/2 0/40  
    第三周 300/300 3/5 40/80

    学习了vim、gcc、gdb指令;

    学习了信息表示和处理

    第五周 200/500 1/6 45/125

    学习了程序的机器级表示

    第六周 150/650 1/7 40/165

    学习了处理器体系结构

    第七周 100/750 1/8 40/205

    学习了存储器层次结构

    第八周 46/796 2/10 40/245

    复习了以前的知识点

    第九周 124/920 1/11 40/285

    学习了系统级I/O的相关内容

  • 相关阅读:
    书摘--可能与不可能的边界
    电影-茶室
    使用unittest,if __name__ == '__main__':里代码不执行的解决办法
    Pycharm中配置鼠标悬停快速提示方法参数
    Python 解决pip使用超时的问题
    Linux性能监控命令——sar详解
    Linux系统管理
    Linux top命令的用法详细详解
    CentOs7排查CPU高占用
    centos 7 查看磁盘io ,找出占用io读写很高的进程
  • 原文地址:https://www.cnblogs.com/sjy519/p/6059370.html
Copyright © 2011-2022 走看看