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

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

    教材学习内容总结

    • 这周老师要求我们学的最差的一章,思来想去,决定写第十章,因为觉得第十章实践的较少再加上在我自己编写程序的过程中,每次只要与文件有关,就需要拿出教材翻阅才可以完成,所以借此机会弥补一下自己的这个不足。

    • 输入/输出是在主存和外部设备(如磁盘驱动器、终端和网络)之间拷贝数据的过程。输入操作时从I/O设备拷贝数据到主存,而输出操作时从主存拷贝数据到I/O设备。

    Unix I/O

    • 一个Linux文件就是一个m个字节的序列。

    • 所有的I/O设备,如网络、磁盘和终端,都被模型化为文件,而所有的输入和输出都被当做对相应的文件的读和写来执行。称为Unix I/O。

    • 所有的输入输出都能以一种统一且一致的方式来执行:
      1.打开文件;
      2.Linux shell创建的每个进程开始时都有三个打开的文件;
      3.改变当前文件的位置;
      4.读写文件;
      5.关闭文件。

    • 打开文件。一个应用程序通过要求内核打开相应的文件来宣告它想要访问一个I/O设备。内核返回一个小的非负整数,称为描述符,在后续对此文件的所有操作中标识这个文件。

    • Linux shell创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0)、标准输出(描述符为1)、标准错误(描述符为2)。

    • 头文件```<unistd.h>定义了常量STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO,它们可用来代替显式的描述符值。

    • 读操作:从文件拷贝n>0个字节到存储器,从当前文件位置k开始,然后将k增加到k+n。

    • 写操作:从存储器拷贝n>0个字节到一个文件,从当前文件位置k开始,然后更新k。

    • 关闭文件:应用在完成了对文件的访问之后,通知内核关闭文件,内核释放文件打开时的数据结构,恢复描述符,释放存储器资源。

    • 在文件系统内,会对文件类型进行标记,以表明其种类。其中一种用来表示普通数据文件,人们常称之为"普通文件"或"纯文本文件",以示与其他种类的文件有所区别。其他文件类型包括设备、管道、套接字、目录以及符号链接。

    • 目录可包含指向文件或其他目录的链接。路径间的链接建立起如图所示的目录层级。

    • 其中每个目录的作用和意义如下图:

    • 每个目录至少包含两条记录:.和..,前者是指向目录自身的链接,后者是指向其上级目录——父目录的链接。除根目录外,每个目录都有父目录。对于根目录而言,..是指向根目录自身的链接。
    • 路径名是由一系列文件名组成的字符串,彼此以"/"分隔,首字符可以为"/"。
    • 路径名应按从左至右的顺序阅读,路径名中每个文件名之前的部分,即为该文件所处目录。可在路径名中任意位置后引入字符串".." ,用以指代路径名中当前位置的父目录。
    • 路径名描述了单根目录层级下的文件位置,又可分为绝对路径名和相对路径名:
      1.绝对路径名以"/"开始,指明文件相对于根目录的位置。
      2.相对路径名定义了相对于进程当前工作目录的文件位置,与绝对路径名相比,相对路径名缺少了起始的"/"。
    • 可使用cd命令来改变shell的当前工作目录,如下图:

    ls以及ls -l指令

    • ls用颜色代表不同文件:蓝色表示目录,绿色表示可执行文件,红色表示压缩文件,浅蓝色表示链接文件,灰色表示其他文件。

    • ls -l以长格式形式在每行显示一个目录或文件。

    • 为了访问文件,系统把用户分为3类:文件的属主(有时,也称为文件的用户)、与文件组(group)ID相匹配的属组成员用户以及其他用户。可为以上3类用户分别设置3种权限(共计9种权限位):只允许查看文件内容的读权限;允许修改文件内容的写权限;允许执行文件的执行权限。

    • ls -l显示结果中,第一个字符表示文件的类型,如下图:

    打开和关闭文件

    • 打开一个已存在的文件或者创建一个新文件:
      int open(char *filename,int flags,mode_t mode) (若成功则返回新文件描述符,若出错为-1)

    • open函数将filename转换为一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。flags参数指明了进程打算如何访问这个文件:
      1.O_RDONLY:只读;
      2.O_WRONLY:只写;
      3.O_RDWR:可读可写。

    • flags参数也可以是一个或者更多位掩码的或,为写提供给一些额外的指示:
      1.O_CREAT:如果文件不存在,就创建它的一个截断的(空的)文件;
      2.O_TRUNC:如果文件已经存在,就截断它;
      3.O_APPEND:在每次写操作前,设置文件位置到文件的结尾处。

    • mode参数指定了新文件的访问权限位。这些位的符号名字如下图所示:

    • 作为上下文的一部分,每个进程都是一个umask,它是通过调用umask函数来设置的。当进程通过带某个mode参数的open函数调用来创建一个新文件时,文件的访问权限位被设置为mode & ~umask。

    • 关闭文件:close()函数:若成功则返回0,不成功则为-1。

    
    #include<unistd.h>
    
    int close(int fd);  //fd:即文件的描述符。 
    
    
    • 注意:关闭一个已关闭的描述符会出错。

    读和写文件

    • 应用程序是通过分别调用read和write函数来执行输入和输出的:
    
    #include <unistd.h>
    ssize_t read(int fd,void *buf,size_t n);
    ssize_t write(int fd,const void *buf,size_t n);
    
    
    • read函数从描述符为fd的当前文件位置拷贝最多n个字节到存储器位置buf,返回值-1表示一个错误。而返回值0表示EOF。否则,返回值表示的是实际传送的字节数量。
    • write函数从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置。
    • 教材上的小练习,代码如下:
    
    #include <stdio.h>  
    #include <unistd.h>  
    #include <string.h>  
    #include <stdlib.h> 
    int main(void)
    {
    	char c;
    	while(read(STDIN_FILENO,&c,1)!=0)
    		write(STDOUT_FILENO,&c,1);
    	exit(0);
    }
    
    
    • 运行结果如下:

    • 一个练习从终端读数据再写回终端,代码如下:

    
    #include <unistd.h>  
    #include <stdlib.h>    
    int main(void)  {
    	   char buf[10];
    	      int n;
    		     n = read(STDIN_FILENO, buf, 10);
    			    if (n < 0) {
    					    perror("read STDIN_FILENO");
    						    exit(1);
    							   }
    				   write(STDOUT_FILENO, buf, n);
    				      return 0; 
    }
    
    
    • 运行结果如下:

    • lseek函数:应用程序能够显式地修改当前文件的位置。

    • 在某些情况下,read和write传送的字节比应用程序要求的要少,其原因如下:
      1.读时遇到EOF。假设我们猪呢比读一个文件,该文件从当前文件位置开始只含有20多个字节,而我们以50个字节的片进行读取。这样一来,下一个read返回的不足值为20,此后的read将通过返回不足值0来发出EOF信号。
      2.从终端读文本行。如果打开文件是与终端相关联的(如键盘和显示器),那么每个read函数将以此传送一个文本行,返回的不足值等于文本行的大小。
      3.读和写网络套接字。如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会引起read和write返回不足值。对Unix管道调用read和write时,也有可能出现不足值,这种进程间的通信机制不在我们讨论的范围之内。

    用RIO包健壮地读写

    • RIO包会自动处理不足值。
    • RIO提供了两类不同的函数:
      1.无缓冲的输入输出函数。这些函数直接在存储器和文件之间传送数据,没有应用级缓冲,他们对将二进制数据读写到网络和从网络读写二进制数据尤其有用。
      2.带缓冲的输入函数。这些函数允许你高效地从文件中读取文本行和二进制数据,这些文件的内容缓存在应用级缓冲区内,类似于像printf这样的标准I/O函数提供的缓冲区。是线程安全的,它在同一个描述符上可以被交错地调用。

    RIO的无缓冲的输入输出函数

    • 通过调用rio_readnrio_writen函数,应用程序可以在内存和文件之间直接传送数据。
    
    ssize_t rio_readn(int fd,void *usrbuf,size_t n);
    ssize_t rio_writen(int fd,void *usrbuf,size_t n);
    (若成功则返回传送成功的字节数,若EOF则为0(只对rio_readn而言),若出错 则为-1)
    
    
    • rio_readn函数从描述符fd的当前文件位置最多传送n和字节到存储器位置usrbuf。类似地,rio_writen函数从位置usrbuf传送n个字节到描述符fd。rio_readn函数在遇到EOF时只能返回一个不足值。rio_writen函数绝不会返回不足值。对于同一个描述符,可以任意交错地调用rio_readnrio_writen

    RIO的带缓冲的输入函数

    • 包装函数rio_readlineb,它从一个内部读缓冲区拷贝一个文本行,当缓冲区变空时,会自动地调用read重新填满缓冲区。对于既包含文本行也包含二进制数据的文件,我们也提供了一个rio_readn带缓冲区的版本,叫做rio_readnb,它从和rio_readlineb一样的读缓冲区中传送原始字节。
    
    #include "csapp.h"
    
    void rio_readinitb(rio_t *rp, int fd);
    
    ssize_t rio_readlineb(rio_t *rp,void *usrbuf, size_t maxlen);
    ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
    
    
    • 每打开一个描述符,都会调用一次rio_readinitb函数。它将描述符fd和地址rp处的一个类型为rio_t的读缓冲区联系起来。
    • rio_readlineb函数从文件rp中读出一个文本行,包括换行符,拷贝到存储器位置usrbuf,并用空字符结束这个文本行。最多读到maxlen-1个字节,最后一个给结尾的空字符。
    • rio_readnb函数从文件rp中读取最多n个字符到内存位置usrbuf中。
    • 一次一行地从标准输入复制一个文本文件到标准输出,代码如下:
    
    #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);
    }
    
    
    • 运行结果如下:

    读取文件元数据

    • 应用程序能通过调用stat和fstat函数,检索到关于文件的信息(元数据)。
    • stat函数以一个文件名作为输入,fstat函数以文件描述符作为输入。
    
    #include <unistd.h>
    #include <sys/stat.h>
    
    int stat(const char *filename,struct stat *buf);
    int fstat(int fd,struct stat *buf);
    
    
    • st_size成员包含了文件的字节数大小。st_mode成员编码了文件访问许可位和文件类型。
    • Lnix提供的宏指令来确定st_mode成员的文件类型:
      1.S_ISREG():普通文件;
      2.S_ISDIR():目录文件;
      3.S_ISSOCK():网络套接字。

    读取目录内容

    • 应用程序可以用readdir系列函数来读取目录的内容。
    
    #include<sys/types.h>
    #include<dirent.h>
    
    DIR *opendir (const char *name);
    
    
    • 函数opendir()以路径名为参数,返回指向目录流的指针。
    • 相对应的就有readdir()函数:
    
    #include<dirent.h>
    struct dirent* readdir(DIR* dirp); 
    
    
    • 一个练习代码如下:
    
    #include<stdio.h>
    #include<dirent.h>
    int main(void)
    {
            DIR *dirptr=NULL;
            int i=1;
            struct dirent *entry;
            if((dirptr = opendir("."))==NULL)
            {
            printf("opendir failed!
    ");
            return 1;
            }
            else
            {
            while(entry=readdir(dirptr))
            {
            printf("filename%d=%s
    ",i,entry->d_name);
            i++;
            }
            closedir(dirptr);
            }
            return 0;
    }
    
    
    • 运行结果如下:

    共享文件

    • 内核用三个相关的数据结构来表示其打开的文件:
      1.描述符表:表项由进程打开的文件描述符来索引的,每个打开的描述符表指向文件表中的一个表项,每个进程有其独立的描述符表。
      2.文件表:打开文件的集合,所有的进程共享,包括当前的文件地址、引用计数及一个指向v-node中对应项的指针,相当于总表。
      3.v-node表:所有进程共享这张表,包含stat结构中的大多数信息,包括st_mode和st_size成员。

    • 上图中描述符1和4通过不同的打开文件表表项来引用两个不同的文件,这是一个典型的情况,是一个没有共享的例子。

    • 上图阐释了多个描述符可以通过不同的文件表表项来引用一个文件。

    • 而父子进程的共享则可以用下图表示

    I/O重定向

    • Linux shell提供了I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来,例如:

    • 重定向使用dup2函数:

    
    #include<unistd.h>
    
    int dup2(int oldfd,int newfd);
            //返回:成功返回非负的描述符,失败返回-1
            
    

    练习题

    • 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”。

    • 10.2 假设磁盘文件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);
    }
    
    
    • 实践结果:

    • 解析:因为是不同的两个文件描述符,每个各自打开了各自的文件表表项,并不共享,所以互不影响,输出仍为f。

    • 10.3 假设磁盘文件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);
    }
    
    
    • 实践结果:

    • 解析:由于子进程会继承父进程的描述符表,因此父子进程打开的是同一个表项,因此无论谁操作下一个就会是在上一个操作的结果上继续操作,因此此处,子进程已经读取了1字节,父进程就在此基础上再读取1字节,因此输出为o。

    • 10.4 如何用dup2将标准输入重定向到描述符5?

    • 首先需要明确标准输入stdin描述符为0,所以应该是dup2(5,0)

    • 10.5 假设磁盘文件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(fd2,&c,1);
    	dup2(fd2,fd1);
    	read(fd1,&c,1);
    	printf("c=%c
    ",c);
    	exit(0);
    }
    
    
    • 实践结果:

    • 解析:由于重定向了,所以相互影响,输出为o。

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

    (一个模板:我看了这一段文字 (引用文字),有这个问题 (提出问题)。 我查了资料,有这些说法(引用说法),根据我的实践,我得到这些经验(描述自己的经验)。 但是我还是不太懂,我的困惑是(说明困惑)。【或者】我反对作者的观点(提出作者的观点,自己的观点,以及理由)。 )

    • 问题1:重定向除了教材中指出的ls > file,之类的,还有哪些?
    • 问题1解决方案:上网搜索,还有如下图所示命令:

    实践:

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

    • 问题1:在实践自己编写的从终端读取数据并打印的代码中,当键入hello world 时就会出错,如下图:

    • 问题1解决方案:上网搜索其原因,发现读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定,在此处hello调用read时睡眠等待,直到终端设备输入了换行符才从read返回,read只读走前几个字符,剩下的字符仍然保存在内核的终端设备输入缓冲区中。hello
      进程打印并退出,这时Shell进程恢复运行,Shell继续从终端读取用户输入的命令,于是读走了终端设备输入缓冲区中剩下的字符d和换行符,把它当成一条命令解释执行,结果发现执行不了,没有d这个命令。在open一个设备时指定了O_NONBLOCK标志,read/write就不会阻塞,可以打开设备文件/dev/tty(表示当前终端),在打开时指定 O_NONBLOCK标志。

    • 问题2:在解决上述问题,编写非阻塞代码时,总是出现下图所示问题:

    • 问题2解决方案:上网搜索,说是要匹配一个fcntl函数,修改并没有解决,也没有找到别的解决方法,待解决。

    代码托管

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

    上周考试错题总结

    • Y86-64中()指令没有访存操作.
      A . rrmovl
      B . irmovq
      C . rmmovq
      D . pushq
      E . jXX
      F . ret
    • 解析:A、B、E,xxmovl是一系列的数据传送指令,jxx
      条件跳转指令。
    • 有关磁盘操作,说法正确的是()
      A . 对磁盘扇区的访问时间包括三个部分中,传送时间最小。
      B . 磁盘以字节为单位读写数据
      C . 磁盘以扇区为单位读写数据
      D . 读写头总处于同一柱面
    • 解析:A、C、D,磁盘上任何时候,所有的续写都位于同一柱面上。
    • 有关RAM的说法,正确的是()
      A .
      SRAM和DRAM掉电后均无法保存里面的内容。
      B .
      DRAM将一个bit存在一个双稳态的存储单元中
      C .
      一般来说,SRAM比DRAM快
      D .
      SRAM常用来作高速缓存
      E .
      DRAM将每一个bit存储为对一个电容充电
      F .
      SRAM需要不断刷新
      G .
      DRAM被组织为二维数组而不是线性数组
    • 解析: A C D E G

    结对及互评

    点评模板:

    • 博客中值得学习的或问题:
      • xxx
      • xxx
      • ...
    • 代码中值得学习的或问题:
      • xxx
      • xxx
      • ...
    • 其他

    本周结对学习情况

    - [20155301](https://home.cnblogs.com/u/fengxingck/)
    - 结对学习内容
        - 对自己不足的内容进行了结对学习。
    

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

    对于自己比较薄弱的文件的内容进行了一次学习,相信对我以后的文件的编程会有所帮助。

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 200/200 2/2 20/20
    第二周 300/500 2/4 18/38
    第三周 500/1000 3/7 22/60
    第四周 300/1300 2/9 30/90
    第五周 300/1300 2/9 30/90
    第六周 706/2006 1/10 50+/140+
    第七周 838/2838 1/11 23/163
    第八周 150/3088 2/13 40/203
    第九周 1235/4323 3/16 27/280

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

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

    • 计划学习时间:20小时

    • 实际学习时间:22小时

    • 改进情况:

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

    参考资料

  • 相关阅读:
    换博客啦
    how2heap学习(二)
    hitcontraining_uaf && hacknote
    WHUCTF PWN题目
    记一次AWD
    how2heap学习(一)
    pwnable_start & ciscn_2019_es_2 & ez_pz_hackover_2016 & pwn2_sctf_2016
    pwnable_start (内联汇编)
    2020年5月计划
    一道逆向出题笔记
  • 原文地址:https://www.cnblogs.com/pingcpingcuo/p/8093889.html
Copyright © 2011-2022 走看看