2017-2018-1 学号20155209 《信息安全系统设计基础》第十四周学习总结
重新深入学习第九章内容
虚拟内存
- 逻辑地址(Logical Address) 是指由程式产生的和段相关的偏移地址部分。例如,你在进行C语言指针编程中,能读取指针变量本身值(&操作),实际上这个值就是逻辑地址,他是相对于你当前进程数据段的地址,不和绝对物理地址相干。只有在Intel实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,Cpu不进行自动地址转换);逻辑也就是在Intel保护模式下程式执行代码段限长内的偏移地址(假定代码段、数据段如果完全相同)。应用程式员仅需和逻辑地址打交道,而分段和分页机制对你来说是完全透明的,仅由系统编程人员涉及。应用程式员虽然自己能直接操作内存,那也只能在操作系统给你分配的内存段操作。
- 线性地址(Linear Address) 是逻辑地址到物理地址变换之间的中间层。程式代码会产生逻辑地址,或说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址能再经变换以产生一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。Intel 80386的线性地址空间容量为4G(2的32次方即32根地址总线寻址)。
- 物理地址(Physical Address) 是指出目前CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。如果启用了分页机制,那么线性地址会使用页目录和页表中的项变换成物理地址。如果没有启用分页机制,那么线性地址就直接成为物理地址了。
- 虚拟内存(Virtual Memory)是指计算机呈现出要比实际拥有的内存大得多的内存量。因此他允许程式员编制并运行比实际系统拥有的内存大得多的程式。这使得许多大型项目也能够在具有有限内存资源的系统上实现。一个非常恰当的比喻是:你不必非常长的轨道就能让一列火车从上海开到北京。你只需要足够长的铁轨(比如说3公里)就能完成这个任务。采取的方法是把后面的铁轨即时铺到火车的前面,只要你的操作足够快并能满足需求,列车就能象在一条完整的轨道上运行。这也就是虚拟内存管理需要完成的任务。在Linux0.11内核中,给每个程式(进程)都划分了总容量为64MB的虚拟内存空间。因此程式的逻辑地址范围是0x0000000到0x4000000。有时我们也把逻辑地址称为 虚拟地址。因为和虚拟内存空间的概念类似,逻辑地址也是和实际物理内存容量无关的。逻辑地址和物理地址的“差距”是0xC0000000,是由于虚拟地址->线性地址->物理地址映射正好差这个值。这个值是由操作系统指定的。机理 逻辑地址(或称为虚拟地址)到线性地址是由CPU的段机制自动转换的。如果没有开启分页管理,则线性地址就是物理地址。如果开启了分页管理,那么系统程式需要参和线性地址到物理地址的转换过程。具体是通过设置页目录表和页表项进行的。
9.1 物理和虚拟地址
- CPU通过生成一个虚拟地址(Virtual address,VA)来访问主存。将虚拟地址转换为物理地址叫做地址翻译(address translation)。地址翻译也需要CPU硬件和操作系统之间的紧密结合。
- CPU芯片上有叫做存储器管理单元(Memory Management Unit,MMU)的专用硬件。利用存储在主存中的查询表来动态翻译虚拟地址。 查询表由操作系统管理。
9.2 地址空间
- 地址空间(adress space)是一个非整数地址的有序集合:{0,1,2,...}
- 如果地址空间中的整数是连续的,那么我们说它是一个线性地址空间(linear address space)。在一个带虚拟存储器的系统中,CPU从一个有N = 2 ^ n个地址空间中生成虚拟地址,这个地址空间称为虚拟地址空间(virtual address space):{0,1,2,3,...,N-1}
- 一个地址空间的大小是由表示最大地址所需要的倍数来描述的。例如,一个包含N=2^n个地址的虚拟地址空间叫做一个n位地址空间。现在系统典型地支持32位或者64位虚拟地址空间是。
- 一个系统还有一个物理地址空间(physical addresss space),它与系统中物理存储器的M字节相对应:{0,1,2,...M-1}
M不要求是2的幂,但是为了简化讨论,我们假设M = 2 ^ m。 - 地址空间的概念是很重要的,因为它清楚地区分了数据对象(字节)和它们的属性(地址)。一旦认识到了这种区别,那么我们就可以将其推广,允许每个数据对象有多个独立的地址,其中每个地址都选自一个不同的地址空间(不连续的意思吗?)。这就是虚拟存储器的基本思想。主存中每个字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。
9.3 虚拟存储器作为缓存的工具
-
虚拟存储器(VM)被组织为一个有存放在磁盘上的N个连续的字节大小的单元组成的数组。每个虚拟页的大小为P = 2p(2的p次方)。物理存储器杯分割为物理页(PP),大小也为P字节(物理页也称为页帧)
-
任意时刻,虚拟页面的集合都分成3个不相交的子集:
-
未分配的:VM系统还有未分配(或创建)的页;不占任何磁盘空间
-
缓存的:当前缓存在物理存储器中的已分配页。
-
未缓存的:没有缓存在物理存储器中的已分配页。
-
DRAM缓存的组织结构:
-
SRAM缓存:表示位于CPU和主存之间的L1、L2和L3高速缓存。
-
DRAM缓存:表示虚拟存储器系统的缓存,它在主存中缓存虚拟页。
-
页表:页表就是一个页表条目(PTE)的数组。每个PTE由一个有效位和一个n位地址字段组成的。有效位表明了该虚拟页是否缓存在DRAM中。(DRAM是全相连的,任意物理页都可以包含任意虚拟页)。
-
页命中:当CPU读取含在VP2中的虚拟存储器的一个字时,VP2是被缓存在DRAM中的,地址翻译硬件将虚拟地址作为一个索引来定位PTE2,并从存储器中读取它。因为设置了有效位,那么地址翻译硬件就将知道VP2是缓存在存储器中的了。所以它使用PTE中的物理存储器地址构造出这个字的物理地址。
-
缺页:DRAM的缓存不命中。
-
交换或页面调度:在磁盘和存储器之间传送页的活动。
-
按需页面调度:页从磁盘换入(或者页面调入)DRAM和从DRAM换出(或者页面调出)磁盘。一直等待,直到最后时刻,也就是有不命中发生时,才换入页面的这种策略。
-
VM缺页(之前):对VP3中的字的引用不命中,从而触发了缺页
-
VM缺页(之后):缺页处理程序选择VP4作为牺牲页,并从磁盘上用VP3的拷贝取代它。在缺页处理程序重新启动导致缺页的指令之后,该指令将从存储器中正常地读取字,而不会再产生异常。
-
颠箥:工作集的大小超出物理存储器的大小。
9.4 虚拟存储器作为存储器管理的工具
-
VM如何为进程提供独立的地址空间。操作系统为系统中的每个进程都维护一个独立的页表:(多虚拟页面可以映射到同一个共享物理页面上)
-
简化链接:独立的空间地址意味着每个进程的存储器映像使用相同的格式。文本节总是从0x08048000(32位)处或0x400000(64位)处开始。然后是数据,bss节,栈。一致性极大简化了链接器的设计和实现。
-
简化加载:在ELF可执行文件中.text和.data节是连续的。要把这些节加载到一个新创建的进程中,linux加载器. 分配虚拟页的一个连续的片,从地址0x08048000处(32bit)开始, 或者从0x400000(64bit),
[把这些虚拟页标记为无效,将页表条目指向目标文件中适当的位置,加载器从不实际拷贝任何数据从磁盘到存储器.] -
简化共享
-
简化存储器分配:malloc在堆空间分配一个适当数字(例如k)个连续的虚拟存储器页面,并且将他们映射到物理存储器中任意位置的k个任意(不一定连续)的物理页面。
-
VM简化了链接和加载、代码和数据共享,以及应用程序的存储器分配。
9.5虚拟存储器作为存储器保护的工具
- SUP位表示进程是否必须运行在内核(超级用户)模式下才能访问该页。运行在内核模式中的进程可以访问任何页面,但是运行在用户模式的进程只允许访问那些SUP为0的页面。
9.6 地址翻译
- 页面命中: CPU硬件执行的步骤
- 第一步:处理器生成一个虚拟地址,并把它传送给MMU。
- 第二步:MMU生成PTE地址,并从高速缓存/主存请求得到它。
- 第三步:高速缓存/主存向MMU返回PTE.
- 第四步;MMU构造物理地址,并把它传送给高速缓存/主存。
- 第五步:高速缓存/主存返回所请求的数据字给处理器。
- 物理缺页:要求硬件和操作系统内核完成:
- 第一步:处理器生成一个虚拟地址,并把它传送给MMU。
- 第二步:MMU生成PTE地址,并从高速缓存/主存请求得到它。
- 第三步:高速缓存/主存向MMU返回PTE.
- 第四步:PTE中的有效位是0,所以MMU触发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序。
- 第五步:缺页处理程序确定出物理存储器中的牺牲页,如果这个页已经被修改了,则把它换出到磁盘。
- 第六步:缺页处理程序页面调入新的页面,并更新存储器中的PTE.
- 第七步:缺页处理程序返回到原来的进程,再次执行导致缺页的指令。CPU将引起缺页的虚拟地址重新发送给MMU.
- TLB(翻译后备缓冲器):有T = 2t(2的t次方),那么TLB索引(TLBI)是由VPN的t个最低位组成的,而TLB标记(TLBT)是由VPN中剩余的位组成的。
- TLB命中
- 第一步:CPU产生一个虚拟地址
- 第二步和第三步:MMU从TLB中取出相应的PTE.
- 第四步:MMU将这个虚拟地址翻译成一个物理地址,并且将它发送到高速缓存/主存。
- 第五步:高速缓存/主存将所请求的数据字返回给CPU.
- 当TLB不命中时,MMU必须从L1缓存中取出相应的PTE(新取出的PTE放在TLB中,可能会覆盖一个已经存在的条目)。
- 多级页表:
- 一级页表中的每个PTE负责映射虚拟地址中的一个4MB的片,这里的每一片都是由1024个连续的页面组成的。比如,PTE0映射第一片,PTE1映射接下来的一片,以此类推。如果地址空间是4GB,1024个PTE已经足够覆盖真整个空间。
9.8 内存映射
-
定义:Linux通过将一个虚拟存储器区域与一个磁盘上对象关联起来,已初始化这个虚拟存储器区域的内容。
-
虚拟存储器区域可以映射到两种类型的对象中的一种:
-
Unix文件系统中的普通文件:一个区域可以映射到一个普通磁盘文件的连续部分。
-
匿名文件:一个区域也可以映射到一个匿名文件,匿名文件是由内核创建的,包含的全是二进制零。由于磁盘和存储器之间并没有实际的数据传送,映射到匿名文件的区域中的页面有时也叫二进制零的页。
-
共享对象
-
一个对象可以被映射到虚拟存储器的一个区域,要么作为共享对象,要么作为私有对象。如果一个进程将一个共享对象映射到它的虚拟地址空间的一个区域内,那么这个进程对这个区域的任何操作,对于那些也把这个共享对象映射它们虚拟存储器的其他进程而言是不可见的。而且,这些变化也会反映在磁盘上的原始对象中。
-
另一方面,对一个映射到私有对象的区域做的改变,对于其他进程来说是不可见的,并且进程岁这个区域所做的任何操作都不会反映在磁盘的对象中。一个映射到共享对象到的虚拟存储器区域也叫共享区域
-
再看共享对象.一个对象可以被映像到虚拟存储器的一个区域,要么作为共享对象,要么作为私有对象.
-
私有对象的写时拷贝
-
再看fork函数:当fork函数被当前进程调用时:
-
1、内核为新进程创建内核数据结构,并分配给它唯一一个PID。
-
2、为了给新进程创建虚拟存储器[创建页目录]。
-
3、创建了当前进程的mm_struct,区域结构和页表的原样拷贝。
-
4、将两个进程的每个页面都标记为[只读]。并给两个区域进程的每个区域结构都标记为[私有的写时拷贝]。
-
注意:[没有对物理存储器进行拷贝,利用的是私有对象的写时拷贝技术。]
-
再看execve函数.假设运行在当前的进程中的程序执行了如下的调用:
-
execve("a.out",NULL,NULL);
-
.out中的程序,用a.out代替当前程序。
-
加载并运行需要以下几个步骤。
-
1、删除已存在的用户区域:删除当前进程虚拟地址的用户部分中已存在的区域结构。
-
2、映射私有区域:为新程序的文本,数据,bss和栈区域创建新的区域结构。所有新的区域结构都是私有的,写时拷贝的。文本和数据区域被映射到a.out文件中的文件和数据区。bss区域是请求二进制零,映射到匿名文件。
-
3、映射共享区域
-
4、设置程序计数器
-
5、execve最后一件事设置PC指向文本区域的入口点。
-
使用mmap函数的用户级存储器映射,Unix进程可以使用mmap函数来创建新的虚拟存储器区域,并将对象映射到这些区域中
#include <unistd.h>
#include <sys/mman.h>
void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset);
返回:若成功时则为指向映射区域的指正,若出错则为MAP_FAILED(-1).
- munmap函数删除虚拟存储器的区域
#include <unistd.h>
#include <sys/mman.h>
void *munmap(void *start,size_t length);
返回:若成功则为0,若出错则为-1
结合本学期操作系统课理解第九章
-
虚拟内存(virtual memory)将用户逻辑内存和物理内存分开。这在现有物理内存有限的情况下,为程序员提供了巨大的虚拟内存。
-
进程的虚拟地址空间就是进程如何在内存中存放的逻辑(或虚拟)视图。通常,该视图为进程从某一个逻辑地址(如地址0)开始,连续存放。
-
根据第八章,物理地址可以按页幁来组织,且分配给进程的物理页帧也可能不是连续的。这就需要内存管理单元(MMU)将逻辑页映射到内存的物理页帧。如上图显示,运行随着动态内存的分配,堆可向上生长。类似地,还允许随着子程序的不断调用,栈可以向下生长。堆与栈之间的巨大空白空间(或hole)为虚拟地址的一部分,只有在堆与栈生长的时候,才需要实际的物理页。包括空白的虚拟地址空间成为稀地址空间,采用稀地址空间的优点是:随着程序的执行,栈或者堆段的生长或需要载入动态链接库(或共享对象)时,这些空白可以填充。
-
除了将逻辑内存与物理内存分开,虚拟内存也允许文件和内存通过共享页而为两个或者多个进程所共享,这样带来了如下的有点:
-
通过将共享对象映射到虚拟地址空间,系统库可为多个进程所共享。虽然每个进程都认为共享库是其虚拟地址空间的一部分,而共享库所用的物理内存的实际页是为所有进程所共享。通常,库是按制度方式来链接每个进程的空间的。
-
类似的,虚拟内存允许进程共享内存。两个或者多个进程之间可以通过使用共享内存来相互通信。虚拟内存允许一个进程创建内存区域,以便与其他进程进行共享。共享该内存区域的进程认为它是其虚拟地址空间的一部分,而事实上这部分是共享的。
-
虚拟内存可允许在用系统调用fork()创建进程期间共享页,从而加快进程的创建。
-
按需调页
-
一个执行程序从磁盘载入内存的时候有两种方法。
-
选择在程序执行时,将整个程序载入到内存中。不过这种方法的问题是可能开始并不需要整个程序在内存中。如有的程序开始时带有一组用户可选的选项。载入整个程序,也就将所有选项的执行代码都载入到内存中,而不管这些选项是否使用。
-
另一种选择是在需要时才调入相应的页。这种技术称为按需调页(demand paging),常为虚拟内存系统所采用。
-
按需调页系统看类似于使用交换的分页系统,进程驻留在第二级存储器上(通常为磁盘)。当需要执行进程时,将它换入内存。不过,不是讲整个进程换入内存,而是使用懒惰交换(lazy swapper)。懒惰交换只有在需要页时,才将它调入内存。由于将进程看做是一系列的页,而不是一个大的连续空间,因此使用交换从技术上来讲并不正确。交换程序(swapper)对整个进程进行操作,而调页程序(pager)只是对进程的单个页进行操作。因此, 在讨论有关按需调页时,需要使用调页程序而不是交换程序。
-
基本概念
-
当换入进程时,调页程序推测在该进程再次换出之前使用到的哪些页,仅仅把需要的页调入内存。从而减少交换时间和所需的物理内存空间。
-
这种方案需要硬件支持区分哪些页在内存,哪些在磁盘。采用有效/无效位来表示。当页表中,一个条目的该位为有效时,表示该页合法且在内存中;反之,可能非法,也可能合法但不在内存中。
-
如果进程从不试图访问标记为无效的页,那么并没有什么影响,因此,如果推测正确且只调入所有真正需要的页,那么进程就可如同所有页都调入内存一样正常运行。
当进程试图访问这些尚未调入内存的页时,会引起页错误陷阱(page-fault trap)。这种情况的处理方式如下: -
检查进程的内部页表(通常与PCB一起保存)。以确定该引用是的合法还是非法的地址访问。
-
如果非法,则终止进程;如果引用有效但是尚未调入页面,则现在进行调入。
-
找到一个空闲帧(如,从空闲帧表中选取一个)。
-
调度一个磁盘操作,以便将所需页调入刚分配的帧
-
磁盘读操作完成后,修改进程的内部表和页表,表示该页已在内存中。
-
重新开始因陷阱而中断的指令。
-
写时复制Copy-on-Write (COW)
-
运行父进程与子进程开始时共享同一页面,这些页面标记为写时复制页,即如果任何一个进程需要对页进行写操作,那就创建一个共享页的副本。
-
因为指标及那些能够被修改的页,所以创建进程的过程更有效率。
-
写时复制所需的空闲也老子一个空闲缓冲池,系统通常用按需填零(zero-fill-on-demand)的技术分配这些页。按需填零在需要分配之前先填零,因此清除了以前的内容。
-
下面的两个过程提箱了进程1修改C前后的物理内存的情况。
问题
疑问:mmap为什么比传统的读写速度要快
解答
- mmap : 将文件内容直接映射到进程的地址空间,通过对这段内存的读写,来达到对文件的读写目的;
read,write : 每次调用都需要从用户态到内核态的切换,且数据需要从用户态拷贝到内核态,然后再写入磁盘,增加了中间步骤 - mmap的缺点 : 不能改变文件长度,无法写入多余的字符。
疑问:操作系统为什么要进行页面置换
解答
- 这是由于操作系统给用户态的应用程序提供了一个虚拟的“大容量”内存空间,而实际的物理内存空间又没有那么大。所以操作系统就就“瞒着”应用程序,只把应用程序中“常用”的数据和代码放在物理内存中,而不常用的数据和代码放在了硬盘这样的存储介质上。如果应用程序访问的是“常用”的数据和代码,那么操作系统已经放置在内存中了,不会出现什么问题。但当应用程序访问它认为应该在内存中的的数据或代码时,如果这些数据或代码不在内存中,则根据上文的介绍,会产生缺页异常。这时,操作系统必须能够应对这种缺页异常,即尽快把应用程序当前需要的数据或代码放到内存中来,然后重新执行应用程序产生异常的访存指令。如果在把硬盘中对应的数据或代码调入内存前,操作系统发现物理内存已经没有空闲空间了,这时操作系统必须把它认为“不常用”的页换出到磁盘上去,以腾出内存空闲空间给应用程序所需的数据或代码。
- 操作系统迟早会碰到没有内存空闲空间而必须要置换出内存中某个“不常用”的页的情况。如何判断内存中哪些是“常用”的页,哪些是“不常用”的页,把“常用”的页保持在内存中,在物理内存空闲空间不够的情况下,把“不常用”的页置换到硬盘上就是页面置换算法着重考虑的问题。容易理解,一个好的页面置换算法会导致缺页异常次数少,也就意味着访问硬盘的次数也少,从而使得应用程序执行的效率就高。
疑问:逻辑地址、物理地址怎么转换,他们和线性地址和虚拟地址有什么区别。
解答
- 物理地址(physical address)
用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。
——这个概念应该是这几个概念中最好理解的一个,但是值得一提的是,虽然可以直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从0字节一直到最大空量逐字节的编号的大数组,然后把这个数组叫做物理地址,但是事实上,这只是一个硬件提供给软件的抽像,内存的寻址方式并不是这样。所以,说它是“与地址总线相对应”,是更贴切一些,不过抛开对物理内存寻址方式的考虑,直接把物理地址与物理的内存一一对应,也是可以接受的。也许错误的理解更利于形而上的抽像。 - 虚拟内存(virtual memory)
这是对整个内存(不要与机器上插那条对上号)的抽像描述。它是相对于物理内存来讲的,可以直接理解成“不直实的”,“假的”内存,例如,一个0x08000000内存地址,它并不对就物理地址上那个大数组中0x08000000 - 1那个地址元素;
之所以是这样,是因为现代操作系统都提供了一种内存管理的抽像,即虚拟内存(virtual memory)。进程使用虚拟内存中的地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址。这个“转换”,是所有问题讨论的关键。
有了这样的抽像,一个程序,就可以使用比真实物理地址大得多的地址空间。(拆东墙,补西墙,银行也是这样子做的),甚至多个进程可以使用相同的地址。不奇怪,因为转换后的物理地址并非相同的。
——可以把连接后的程序反编译看一下,发现连接器已经为程序分配了一个地址,例如,要调用某个函数A,代码不是call A,而是call 0x0811111111 ,也就是说,函数A的地址已经被定下来了。没有这样的“转换”,没有虚拟地址的概念,这样做是根本行不通的。
打住了,这个问题再说下去,就收不住了。 - 逻辑地址(logical address)
Intel为了兼容,将远古时代的段式内存管理方式保留了下来。逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址。以上例,我们说的连接器为A分配的0x08111111这个地址就是逻辑地址。
——不过不好意思,这样说,好像又违背了Intel中段式管理中,对逻辑地址要求,“一个逻辑地址,是由一个段标识符加上一个指定段内相对地址的偏移量,表示为 [段标识符:段内偏移量],也就是说,上例中那个0x08111111,应该表示为[A的代码段标识符: 0x08111111],这样,才完整一些” - 线性地址(linear address)或也叫虚拟地址(virtual address)
跟逻辑地址类似,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件页式内存的转换前地址。 - 参考博客
疑问:缓存和虚拟内存的区别?虚拟内存与磁盘缓存是否矛盾?高速缓存和虚拟内存发展和联系?
解答
- 由于CPU的运算速度愈来愈快,主存储器(DRAM)的数据存取速度常无法跟上CPU的速度,因而影响计算机的执行效率,如果在CPU与主存储器之间,使用速度最快之SRAM来作为CPU的数据快取区,将可大幅提升系统的执行效率,而且透过Cache来事先读取CPU可能需要的数据,可避免主存储器与速度更慢的辅助内存的频繁存取数据,对系统的执行效率也大有帮助。
- 不过因SRAM比DRAM贵太多,如果主存储器全采用SRAM则系统造价太高,所以一般皆只安装512KB~1MB的Cache。Cache的应用除了加在CPU与主存储器之间外,硬盘、打印机、CD-ROM等外围设备也都会加上Cache来提升该设备的数据存取效率。
- 用于 DNS 和 WINS,用于远程主机的最近已解析名称的资源记录的本地信息存储。通常,高速缓存在计算机查询和解析名称被动态地创建。它也有助于优化解析被查询名称所需的时间。
- 将最近使用过的数据值临时存储于内存中的某个特殊池中以便于以后更快地进行访问的过程。对于 DNS,一般指 DNS 查询解析过程中 DNS 服务器存储得自 DNS 名称空间的信息的能力。(例如,DNS 服务器可以高速缓存从其他 DNS 服务器收到的 DNS 记录。) 也可以在 DNS 客户服务中使用高速缓存,将其作为 DNS 客户端保存在最近的查询过程中得到的信息高速缓存的方法。
给你的结对学习搭档讲解你的总结并获取反馈
- 这周要求学习之前学的最不好的一章,我选择的是第9章,第九周大多数都是理论性的知识,是计算机操作系统的一个原理,在与同伴的互相交流下更好的理解了。
- 20155230
- 结对照片
- 结对学习内容
- 共同解决了学习中遇到的问题,并在博客问题板块体现。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 20/20 | 1/1 | 20/20小时 | |
第二周 | 50/70 | 1/2 | 5/25小时 | |
第三周 | 100/170 | 1/3 | 10/35小时 | |
第四周 | 68/238 | 1/4 | 20/55小时 | |
第五周 | 40/278 | 1/5 | 10/65小时 | |
第六周 | 100/378 | 1/6 | 10/75小时 | |
第七周 | 30/678 | 1/7 | 10/85小时 | |
第八周 | 200/878 | 1/8 | 10/95小时 | |
第九周 | 300/1178 | 1/9 | 10/105小时 | |
第十周 | 278/1456 | 1/10 | 10/115小时 | |
第十一周 | 307/1763 | 1/11 | 10/125小时 | |
第十二周 | 457/2220 | 1/12 | 10/135小时 | |
第十三周 | 612/2832 | 1/14 | 10/145小时 | |
第十四周 | 231/3063 | 1/15 | 10/155小时 |
-
计划学习时间:20小时
-
实际学习时间:10小时
-
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)