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

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

    【学习时间:6小时】

    【学习内容:CHAPTER 10——系统级I/O】

    一、课本内容

    1.每个unix文件都是一个m字节的序列;所有I/O设备如网络、磁盘和终端都被模型化为文件,而输入和输出就是对这些文件的读写操作。

    2.unix系统中输入输出的操作:

    • 打开文件:一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符。unix系统创建每个进程的时候都有三个打开的文件:标准输入;标准输出,标准错误。
    • 改变当前的文件位置。对于每个打开的文件,内核保持着一个文件位置k(从文件开头起始的字节偏移量)。
    • 读写文件。读操作就是从文件拷贝n>0个字节到存储器,从当前文件位置k开始,然后将k增加到k+n。
    • 关闭文件。应用通知内核关闭这个文件;作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池当中。

    【把I/O抽象成文件,其实是把系统内的一切操作都变成对文件(字节序列)的操作;这样极大地简洁了各类动作。上述对文件的描述,其实是输入输出类型的“文件操作”,分别对应的是进行I/O、读写操作。】

    3.打开和关闭文件

    1. 打开文件:

      fd = Open("文件名",flag位——表示访问方式及额外提示,mode参数);//出错的时候返回-1

      1. mode参数指定新文件的访问权限位。作为上下文的一部分,每个进程都有一个umask;当进程通过带某个带mode参数的open函数用来创建一个新文件的时候,文件的访问权限位被设置为mode & ~umask。
      2. fd是返回的文件描述符(数字),总是返回在进程中当前没有打开的最小描述符。
      3. flag参数可以是一位或者多位掩码的或,如:O_CREAT,表示如果文件不存在,就创建它的一个截断的文件。
    2. 关闭文件:

      int close(int fd);//若成功则返回0,不成功则为-1

    【这里的打开文件时返回值fd的详细介绍(比如什么是“在进程中当前没有打开的最小描述符”可以参考练习题10.1)】

    4.读写文件

    • 读函数

      ssize_t read(int fd,void *buf,size_t n);//成功则返回n;EOF返回0;出错返回-1

    read函数从描述符为fd的当前文件位置拷贝最多n个字节到存储器位置buf。

    【什么是EOF?就是给定了m字节大小的文件;在从k字节位置开始读或者写的时候,发现k>=m】

    • 写函数

      ssize_t write(int fd,const void *buf,size_t n);

    【ssziet,sizet有什么区别?前者被定义为int,有符号;后者被定义成unsigned int,无符号】

    5.RIO包

    • 无缓冲的输入输出函数。直接在存储器和文件之间传送数据(允许被中断的字节调用,并在必要的时候重启它们)。

      ssizet riowriten(int fd,const void *usrbuf,size_t n);

      ssizet riowriten(int fd,const void *usrbuf,size_t n);

    rio__writen函数遇到EOF的时候返回0;

    rio__readn遇到EOF的时候返回不足值(即 不足n的那个部分的字节数)。

    • 带缓冲的输入函数。允许用户高效地从文件中读取文本行和二进制数据(之前是一次读一个字节,应用这个函数之后,可以一次读一行函数)。

    原理:函数从内部缓冲区中拷贝一个文本行,当缓冲区变空的时候,会自动地调用read重新填满缓冲区。

    6.带缓冲的输入函数详解

    • rioreadinitb(riot *rp,int fd);

    每打开一个描述符都会调用一次该函数,它将描述符fd和地址rp处的类型为rio_t的缓冲区联系起来。

    • rioreadnb(riot *rp,void *usrbuf,size_t n) ;

    从文件rp中最多读n个字节到存储器位置usrbuf。对同一描述符,rioreadnb和rioreadlineb的调用可以交叉进行。

    • ssizet readlineb(riot *rp,void *usrbuf,size_t maxlen);

    从文件rp中读取一个文本行(包括结尾的换行符),将它拷贝到存储器位置usrbuf,并用空字符来结束这个文本行。

    7.RIO读程序核心:rio-read函数

    static ssize_t rio_read(rio_t *rp,char *usrbuf,size_t n)
    {
        int cnt;
        while(rp->rio_cnt<=0)//如果缓冲区为空,先调用函数填满缓冲区再读数据
        {
            rp->rio_cnt=read(rp->rio_fd,rp->rio_buf,sizeof(rp->rio_buf));//调用read函数填满缓冲区
            if(rp->rio_cnt<0)//排除文件读不出数据的情况
            {
                if(error != EINTR)
                {
                    return -1;
                }
            }
            else if(rp->rio_cnt=0)
                return 0;
            else 
                rp->rio_bufptr = rp->rio_buf;//更新现在读到的位置
        }
        cnt=n;
        if(rp->rio_cnt<n)
            cnt=rp->rio_cnt;//以上三步,将n与rp->rio_cnt中较小的值赋给cnt
        memcpy(usrbuf,rp->rio_bufptr,cnt);把读缓冲区的内容拷贝到用户缓冲区
        rp->rio_bufptr+=cnt;
        rp->rio_cnt-=cnt;
        return cnt;
    }
    

    8.读取文件元数据

    • 应用程序能够通过调用stat和fstat函数,检索到关于文件的信息(元数据)。
    • 函数格式:

      #include <unistd.h>
      #include <sys/stat.h>
      int stat(cost char *filename,struc sta *buf);
      int fstat(int fd,struct stat *buf);
      
    • 这两个函数都需要填写stat结构体的各个成员。这里举两个例子:stsize包含了文件的字节数大小;stmode则编码了文件访问许可位和文件类型(目录文件:包含关于其他文件的信息;套接字是一种用来通过网络与其他进程通信的文件)。

    9.表示打开文件的三个数据结构

    • 描述符表。每个进程都有独立的描述符表;它的表项是由进程打开的文件描述符来索引的。
    • v-node表。所有进程共享。每个表项包含stat结构中的大多数信息。
    • 文件表。表示打开文件的集合;所有的进程共享。表项有:文件位置、引用计数、指向v-node表中对应表项的指针。

    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】

    2.下面代码的功能是什么?

    #include "csapp.h"
    int main(void)
    {
        char c;
        while(Read(STDIN_FILENO,&C,1) !=0)
            Write(STDOUT_FILENO,&c,1);
        exit(0);
    }
    

    【每次一个字节,从标准输入传递到标准输出】

    3.假设磁盘文件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。】

    4.就像前面那样,磁盘文件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。】

    心得

    本次学习的章节内容较少,然而值得深入挖掘的代码部分也是十分重要。我之间就觉得,读代码是一件很有意思的事情——在逐行推敲之后恍然大悟,好像柳暗花明。本章节的代码设计的都是对“基本操作”的分解(原来简单的输入和输出背后也是这么多有序的步骤);读起来更是饶有趣味。

    另外,在使用虚拟机的时候,我发现虚拟机平台提示“硬盘位置改动”导致linux系统不可用;在百度了资料之后,利用虚拟介质管理修复了错误。

  • 相关阅读:
    学习笔记: yield迭代器
    学习笔记: 委托解析和封装,事件及应用
    学习笔记: MD5/DES/RSA三类加密,SSL协议解析
    学习笔记: Expression表达式目录树详解和扩展封装
    学习笔记: Expression表达式目录树详解和扩展封装
    学习笔记: IO操作及序列化
    数据类型转换
    短路运算(逻辑运算是短路运算中最常见的一种)
    清除浮动
    css初始化
  • 原文地址:https://www.cnblogs.com/lwr-/p/4940746.html
Copyright © 2011-2022 走看看