• 2017-2018-1 20155315 《信息安全系统设计基础》第13周学习总结


    2017-2018-1 20155315 《信息安全系统设计基础》第13周学习总结

    教材学习内容总结

    • 本周老师让我们学习的是自己觉得最终要的一章,我学习的是第十章。在我看来,不论是什么产品,都存在设计端与使用端,更确切的说,正因为有使用端,才需要设计端。因此,使用端的使用感受最为重要,而作为一个对外的平台与程序,I/O系统是基础和核心,也因此我选择系统级I/O进行深入学习。
    • 主要知识点:
      • 输入是从I/O设备复制数据到主存
      • 输出是从主存复制数据到I/O设备

    UnixI/O

    • 每个unix文件都是一个m字节的序列。
    • unixI/O是由内核提供的。
    • I/O设备都是文件,输入就是写文件,输出就是读文件。
    • 程序要求内核打开文件来说明它想要访问相应的I/O设备,内存返回描述符,fd = Open("文件名",flag参数,mode参数);,此时fd就是一个描述符。
    • 每个进程都有三个文件,头文件是<unistd.h>,输入返回0,输出返回1,错误返回2。
    • 文件位置k是字节偏移量,初始为0,用seek查找当前位置。
    • 读是将文件复制n个字节到内存中,文件位置增加到k+n,当n大于文件字节的时候,会返回EOF错误,表示读到文章结尾。

    文件

    • 普通文件分文本文件和二进制文件,文本文件致函ASCII或Unicode字符。
    • 对内核而言,文本文件和二进制文件没有区别
    • 文本文件每一行都是字符序列
    • 目录是一组包含链接的文件。
    • 路径名是一个字符串,有绝对路径和相对路径。

    打开和关闭文件

    • 打开文件
    int open(char *filename, int flags, mode_t mode)
    
    flags 行为
    O_RDONLY 只读
    O_WRONLY 只写
    O_RDWR 可读可写
    O_CREAT 文件不存在即创建一个截断文件
    O_TRUNC 文件存在就截断
    O_APPEND 写之前设置是文件在文件结尾
    • 关闭文件
    int close(int fd);
    

    读写文件

    • 读文件
    ssize_t Read(int fd, void *buf, size_t count);
    
    返回值 意义
    -1 错误
    0 读到文章末尾
    其他 实际传送的字节数量
    • 写文件
    ssize_t Write(int fd, const void *buf, size_t count);
    
    • 一次一个字节地从标准输入复制到标准输出
    #include "csapp.h"
    int main(void)
    {
    char c;
    while(read(STDIN_FILENO,&c,1)!=0)
    write(STDOUT_FILENO,&c,1);
    exit(0);
    }
    

    • ssize_t被定义为long,是有符号的;size_t被定义为unsigned long,是无符号数
    • 可能出现不足值
      • 读时遇到FOF
      • 从终端读文本行
      • 读和写网络套接字

    RIO(Robust I/O)

    • 无缓冲的输入输出:直接在内存和文件之间传送数据
    ssize_t rio_readn(int fd, void *usrbuf, size_t n);
    ssize_t rio_writen(int fd, void *usrbuf, size_t n);
    
    • read遇到EOF只能返回一个不足值,write不会返回不足值。
    • 被中断时函数会重启read或write
    • 带缓冲的输入输出:数据存在缓冲区
    ssize_t	rio_readnb(rio_t *rp, void *usrbuf, size_t n);
    ssize_t	rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
    
    • readlineb读取一行文本到缓冲区(包括换行符),用NULL(0)结束,最多读maxlen-1个字节
    • rio_readnb读取n个字节到缓冲区
    • 一次一行地从标准输入复制一个文本文件到标准输出
    #include "csapp.h"
    int main(int argc,char **argv)
    {
    int n;
    rio_t rio;
    char buf[MAXLINE];
    rio_readinitb(&rio,STDIN_FILENO);
    while((n=rio_readlineb(&rio,buf,MAXLINE))!=0)
    rio_writen(STDOUT_FILENO,buf,n);
    }
    


    我的理解:读文件主要有三部分。一是init函数初始化,将文件描述符与读缓存区联系在一起;二是readline或readnb将文本复制到读缓存区;三是内部的read函数,当缓冲区为空,调用read填满,非空的时候,read从读缓存区复制字节到用户缓冲区,也就是说,涉及到两个缓冲。

    读取

    读取元数据

    • stat函数以一个文件名作为输入,fstat以文件描述符作为输入
    st_mode 文件访问许可位和文件类型
    S_ISREG(m) 普通文件
    S_ISDIR(m) 目录文件
    S_ISSOCK(m) 网络套接字
    • 查询和处理一个文件的st_mode位。
    #include "csapp.h"
    int main(int argc,char **argv)
    {
    struct stat stat;
    char *type,*readok;
    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);
    }
    

    读取目录

    opendir
    readdir//返回inode和文件名,出错返回NULL,设置errno
    

    共享文件

    • 描述符表:每个进程有一个独立的描述表,相当于本地文件
    • 文件表:打开文件的集合,所有的进程共享,包括当前的文件地址、引用计数及一个指向v-node中对应项的指针,相当于总表
    • v-node表:所有进程共享,包含stat中的大多数信息,相当于详情。
    • 多个描述符可通过不同的文件表表项来引用同一个文件。
    • 多进程时,子进程继承父进程,复制父进程的描述符表,在内核删除相应表项前,父子进程都要关闭描述符。

    重定向

    dup2函数,复制oldfd到newfd,若newfd已经打开,dup2会先关闭它。

    标准I/O

    练习题

    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进程生命周期开始时,打开的描述符赋给了stdin(描述符0)、stdout((描述符1)和stderr(描述符2)。open函数总是返回最低的未打开的描述符,所以第一次调用open会返回描述符3,调用close函数会释放描述符3。最后对open的调用会返回描述符3,因此程序的翰出是“fd2=3”。当文件不存在是,输出“fd2=-1”

    10.2

    #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);
    }
    
    • 运行结果
    • 答案:fd2读操作读取foobar.txt的第一个字节。

    10.3

    #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);
    }
    
    
    • 运行结果
    • 答案:描述符fd在父子进程中都指向同一个打开文件表表项,当子进程读取文件的第一个字节时,文件位置加1。因此,父进程会读取第二个字节,也就是o

    10.4

    如何用dup2将标准输入重定向到描述符5?
    回答:dup2(5,0)

    10.5

    #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(fd2,&c,1);
    dup2(fd2,fd1);
    read(fd1,&c,1);
    printf("c=%c
    ",c);
    exit(0);
    }
    
    • 答案:将fd1重定向到fd2,因此第二个fd1指的是fd2+1
    • 运行结果

      回答:将fdl重定向到了fd2,输出实际上是c=o

    教材学习中的问题和解决过程

    问题1: 使用带缓存的RIO包的时候,为什么要先设定一个缓存区,再将其中的数据转移到缓冲区呢?

    解决1:

    带缓冲的I/O是指进程对输入输出流进行了改进,提供了一个流缓冲,当用fwrite函数网磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件,比如流缓冲区满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。

    也就是说,如果直接将数据写入缓存区就系统调用,与不带缓存的RIO是一样的,设定双重缓冲是为保证算法的效率,减少系统调用的次数。

    问题2: 为什么使用一个static缓冲区就不是线程安全的?什么是线程安全?

    解决2:

    非线程安全是指多线程操作同一个对象可能会出现问题。而线程安全则是多线程操作同一个对象不会有问题。

    我的理解:static缓冲区是静态的,就好像是静态变量一样,本地操作即单一线程调用的时候是不会出错的,但它不是可以共享的,也就是说其他线程调用的时候就可能出现问题。

    代码学习中的问题和解决过程

    问题1: 编译书p629代码时

    解决1: 将csapp.c、csapp.h和cpfile.c编译在一起gcc csapp.h csapp.c cpfile.c -o cpfile -lpthread

    问题2: 编译书p634代码时

    解决2: 使用man readdir发现有一个系统调用版本和c版本,使用man 3 readdir发现在csapp.h中缺少dirent.h头文件,加上之后编译通过,运行成功

    代码托管

    (statistics.sh脚本的运行结果截图)

    结对及互评

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

    认真学习I/O相关知识后,知道了输入输出的更多知识,也了解到系统在处理输入输出的时候会操作什么改变什么,希望对今后的学习有所帮助。

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 5/5 1/1 25/25
    第二周 236/241 3/4 30/55
    第三周 169/410 2/6 30/85
    第四周 169/410 2/8 50/135
    第五周 1177/1587 2/10 30/165
    第六周 1826/3413 2/12 30/195
    第七周 977/4390 3/15 30/225
    第八周 977/4390 2/17 30/255
    第九周 977/4390 2/19 30/285
    第十周 977/4390 0/15 30/315
    第十一周 977/4390 2/21 25/335
    第十二周 977/4390 0/21 25/360
    第十三周 977/4390 2/23 30/390
    • 计划学习时间:20小时
    • 实际学习时间:30小时

    (有空多看看现代软件工程 课件
    软件工程师能力自我评价表
    )

    参考资料

  • 相关阅读:
    Stimulsoft Reports筛选数据来绑定显示2个报表
    随便写点吧
    盒模型、文档流
    css选择器、权重
    css3转换
    html
    css新起点
    css里的那些事儿
    【谭老师讲堂】零基础如何学好Java——交流
    【谭老师讲堂】零基础如何学好Java——棒棒糖
  • 原文地址:https://www.cnblogs.com/-zyl/p/8052702.html
Copyright © 2020-2023  润新知