zoukankan      html  css  js  c++  java
  • 2017-2018-1 20155330 《信息安全系统设计基础》第13周学习总结

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

    教材学习内容总结

    Unix I/O

    • 基本概念:所有的I/O设备都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O。
    • Unix I/O所有的输入和输出都能以一种统一且一致的方式执行:
      • 打开文件。

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

      • Linux shell创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0)、标准输出(描述符为1)和标准错误(描述符为2)。头文件<unistd.h>定义常量STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO用来代替显式的描述符值。

      • 改变当前的文件位置。

      • 读写文件。

      • 关闭文件。

    打开和关闭文件

    1.open函数

    (1)函数定义:

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

    (2)参数解析:

    flags:指明进程打算如何访问这个文件,主要参数:

    O_RDONLY:只读
    O_WRONLY:只写
    O_RDWR:可读可写

    O_CREAT:文件不存在,就创建新文件
    O_TRUNC:如果文件存在,就截断它
    O_APPEND:写操作前设置文件位置到结尾处
    这些值可以用或连接起来。
    mode:指定了新文件的访问权限位,符号名称如下:

    读和写文件

    读和写文件
    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个字节到当前文件位置

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

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

    > ssize_t和size_t有什么区别?
    
    ![](http://images2017.cnblogs.com/blog/1071510/201712/1071510-20171217231718421-1075649569.png)
    

    4.不足值

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

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

    用RIO包健壮的读写

    RIO,Robust I/O,自动处理不足值。

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

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

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

    参数:

    fd:文件描述符
    usrbuf:存储器位置
    n:传送的字节数
    返回值:

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

    rio_writen成功则返回传送的字节数,出错为-1,没有不足值。

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

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

    一个文本行就是一个由换行符结尾的ASCII码字符序列。

    使用RIO函数实现一次一行的从标准输入复制一个文本文件到标准输出。

    #include "csapp.h"
    
    int main(int argc, char **argv) 
    {
        int n;
        rio_t rio;
        char buf[MAXLINE];
    
        Rio_readinitb(&rio, STDIN_FILENO);//连接标准输入和rio地址
        while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) //当成功返回时,将rio中的内容拷贝到存储器位置buf中,最多读maxline-1
        Rio_writen(STDOUT_FILENO, buf, n);//把存储器位置中的数据拷贝到标注输出中。
        exit(0);
    }
    

    读缓冲区格式,并初始化其rio_readinitb函数。

    #define RIO_BUFSIZE 8192
    typedef struct {
        int rio_fd;                /* descriptor for this internal buf */
        int rio_cnt;               /* unread bytes in internal buf */
        char *rio_bufptr;          /* next unread byte in internal buf */
        char rio_buf[RIO_BUFSIZE]; /* internal buffer */
    } rio_t;
    
    void rio_readinitb(rio_t *rp, int fd) 
    {
        rp->rio_fd = fd;  
        rp->rio_cnt = 0;  
        rp->rio_bufptr = rp->rio_buf;
    }
    

    RIO读程序的核心是rio_read函数

    static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
    {
        int cnt;
    
        while (rp->rio_cnt <= 0) {  /* 如果缓存区为空,调用read填满它 */
        rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, 
                   sizeof(rp->rio_buf));
        if (rp->rio_cnt < 0) {
            if (errno != EINTR) /* 出错返回-1*/
            return -1;
        }
        else if (rp->rio_cnt == 0)  /* EOF返回0 */
            return 0;
        else 
            rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */
        }
    
        /* 一旦缓存区非空,就从读缓存区拷贝n和rp->rio_cnt中较小值个字节到用户缓存区,并且返回拷贝的字节数 */
        cnt = n;          
        if (rp->rio_cnt < n)   
        cnt = rp->rio_cnt;
        memcpy(usrbuf, rp->rio_bufptr, cnt);
        rp->rio_bufptr += cnt;
        rp->rio_cnt -= cnt;
        return cnt;
    }
    

    rio_readnb函数

    ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) 
    {
        size_t nleft = n;
        ssize_t nread;
        char *bufp = usrbuf;
        
        while (nleft > 0) {
        if ((nread = rio_read(rp, bufp, nleft)) < 0) {
            if (errno == EINTR) 
            nread = 0;      /* 调用read填充 */
            else
            return -1;      /* 错误,返回-1 */ 
        } 
        else if (nread == 0)
            break;              /* EOF */
        nleft -= nread;
        bufp += nread;
        }
        return (n - nleft);         /* 返回成功传送的字节数*/
    }
    

    rio_readlineb函数

    ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) 
    {
        int n, rc;
        char c, *bufp = usrbuf;
    
        for (n = 1; n < maxlen; n++) { //最多是maxlen-1个
        if ((rc = rio_read(rp, &c, 1)) == 1) {
            *bufp++ = c;
            if (c == '
    ')//找到换行符,就退出
            break;
        } else if (rc == 0) {
            if (n == 1)
            return 0; /* EOF,并且没有读到数据 */
            else
            break;    /* EOF,有数据,出现不足值 */
        } else
            return -1;    /* 错误,返回-1 */
        }
        *bufp = 0;
        return n;//返回成功传送的字节数
    }
    

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

    在stat数据结构中需要注意的有两个变量,st_mode和st_size。

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

    类型 作用 函数
    普通文件 二进制或文本文件(对内核没差) S_ISREG()
    目录文件 关于其他文件的信息 S_ISDIR()
    套接字 通过网络与其他进程通信的文件 S_ISSOCK()
    • 查询和处理一个文件的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);
    }
    

    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,如果后者被打开,则在拷贝前关闭它。

    标准I/O

    1. 标准I/O库:

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

    fopen、fclose,打开和关闭文件

    fread、fwrite,读和写字节

    fgets、fputs,读和写字符串

    scanf、printf,复杂的格式化的I/O函数

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

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

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

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

    套接字

    网络套接字上最好不要使用标准I/O函数,而是使用RIO函数,原因:

    如果没有清楚缓存区,输入函数后面不能接输出函数,输出函数后面也不能接输入函数,而对套接字使用lseek是非法的,打开两个流有很麻烦,所以!在网络套接字上不要使用标准I/O函数来进行输入和输出!

    • 错误处理
      附录A中主要讲了这本书中的错误处理方式,有一个方法——错误处理包装函数,这个思想很有意思,相当于给基本函数再套上一层皮,然后run这个皮,发现了错误就终止,完全正确的话就跟没有这层皮一样。
    1. 错误处理风格

    (1)Unix风格

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

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

    (2)Posix风格

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

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

    (3)DNS风格

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

    (4)错误报告函数

    void unix_error(char *msg) /* unix-style error */
    {
        fprintf(stderr, "%s: %s
    ", msg, strerror(errno));
        exit(0);
    }
    /* $end unixerror */
    
    void posix_error(int code, char *msg) /* posix-style error */
    {
        fprintf(stderr, "%s: %s
    ", msg, strerror(code));
        exit(0);
    }
    
    void dns_error(char *msg) /* dns-style error */
    {
        fprintf(stderr, "%s: DNS error %d
    ", msg, h_errno);
        exit(0);
    }
    
    void app_error(char *msg) /* application error */
    {
        fprintf(stderr, "%s
    ", msg);
        exit(0);
    }
    

    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;
    }
    

    本章习题

      • 运行结果:
      • 运行结果:
      • 运行结果:
    1. 调用dup2(5,0)/dup2(5,STDIN_FILENO)

      • 运行结果:

    代码托管

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 0/0 1/1 10/10
    第二周 63/63 1/2 8/18
    第三周 31/94 1/3 18/36
    第四周 265/329 1/4 17/53
    第五周 106/435 2/6 18/71
    第六周 211/646 2/8 21/92
    第七周 1420/2066 2/10 17/109
    第八周 1061/3127 1/11 17/126
    第九周 1458/4585 3/14 20/146
    第十周 1410/5995 1/15 20/166
    第十一周 779/6774 2/17 18/184
    第十三周 326/7100 2/19 20/204

    尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
    耗时估计的公式
    :Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。

    参考:软件工程软件的估计为什么这么难软件工程 估计方法

    • 计划学习时间:20小时

    • 实际学习时间:20小时

    参考资料

  • 相关阅读:
    MFC单文档中进行plc点云显示
    modbus发送和接收
    测试左移和测试右移
    2021软件测试笔试题
    202106月份总结测试开发面试题
    KuaiShou面试题
    阿拉伯数学手写体。
    GeminiScrollbar
    vue-core-video-player
    webpack
  • 原文地址:https://www.cnblogs.com/ashin-kl/p/8053661.html
Copyright © 2011-2022 走看看