2020校招备战日记4.22 ---- 二叉树遍历,适配器模式,模板方法模式,组合模式,观察者模式,原型模式,进程虚拟内存,内存管理

0.目标完成情况

  • LeetCode两道题
  • 虚拟内存剩下的部分两天内看完。
  • 侯捷C++一定要看一节。
  • 可以再看一点Linux扫盲课程。
  • 严格遵循时间表来作息。

今天的工作量还是挺多的。主要是早上起得早。一日之计在于晨呀。

1. 白天所学回顾

由于今天内容较多,时间不早了,我就大概说一下,明天再说得仔细一点。

1.1 LeetCode题目

二叉树的右视图
这题不算难。就是二叉树的层序遍历的第二种方法,在每次遍历之前,要记录当前队列中有多少个元素,这么多元素作为一组来遍历即可。每一组的最右边一个作为输出。

除此之外还复习了二叉树三种非递归遍历,还录了三个视频,压缩了一下上传到b站了,耽误了不少时间。不提了。

1.2 五种设计模式

侯捷用了几个例子来阐述这些设计模式。
适配器模式, C++的queue的设计,就是适配器模式。因为queue类当中有一个deque对象,queue的所有功能,均由deque对象来实现,queue只要改改名字即可。
模板方法模式, MFC打开文件的框架设计。所谓框架就是模板,框架实现了通用的功能,而框架内部有个别方法,需要子类自己实现。
组合模式, 文件和文件夹的建模,文件夹中既可以放文件,也可以放文件夹,那么就抽象出一个文件和文件夹的父类。
观察者模式, 一个文件有多个视图的建模。文件对象拥有多个观察者对象的指针,而观察者可以是多态的,因此可以有多个视图,同时文件还有一个通知方法,每当文件update的时候,都会notify所有的观察者,从而观察者根据自己的动态类型做相应动作。
原型模式 , 父类始终会自动拥有子类的一个原型,而不用预先知道子类的名称和具体类型。子类拥有一个静态对象,这个静态对象就是原型,还有一个私有的构造函数,用来自动生成这个静态对象。并且,在这个构造函数执行过后,会自动执行一个挂载函数,挂载函数是父类已经写好的,从而父类就可以访问到这个子类对象(其实只是拥有了静态的子类对象的指针)了,也就是拥有了子类的原型。当父类需要使用子类原型的时候,就调用子类的一个clone函数,而这个clone函数使用了一个新的、不会调用挂载函数的构造函数,从而可以得到一个副本。使用副本的原因是,我们不希望改变原型。

以上这些设计模式灵活运用了类之间的三种关系,到处都是虚函数、动态绑定,如果没有代码实践的话,理解是不会很深入的,有时间做一点练习吧。

1.3 进程虚拟内存

这里主要讲了两个问题,一个是虚拟内存有什么用,一个是虚拟内存中地址翻译的过程。

虚拟内存的作用
  1. 作为高速缓存。由于多个进程在物理上共用相同的DRAM,而又因为局部性原理,我们通常只会将虚拟内存的一部分缓存在物理内存中,或者可以再添加一层SRAM高速缓存。然后通过页表完成虚拟地址到物理地址的映射,从而让进程可以引用很大的虚拟地址,却不用真的将那些地址的内容都拷贝进内存。
  2. 作为内存管理工具。从程序的连接、加载、执行到动态库的共享,都可以被虚拟内存简化。
地址翻译的过程(不命中)
  1. cpu拿到一个虚拟地址,将其交给MMU单元
  2. MMU根据虚拟地址的页号部分,找到其在页表中的条目地址。(页表在内存中)
  3. 去页表中读取条目内容
  4. 发现条目的标志位为0,所以该虚拟页为缓存在物理页中
  5. 触发缺页故障,进入缺页处理程序。缺页处理程序会做一些判定,当判定这个故障是由于“对一条合法地址的合法访问导致”的时候,就会选择一个牺牲页,如果该牺牲页发生过修改,就将其换出到磁盘。否则就直接将目标虚拟页换入到牺牲页的位置。然后修改页表项。
  6. 缺页处理程序返回。cpu重新执行引起故障的指令,此时已经不会发生缺页故障了。

对于命中的情况,就是直接将从页表项中取到相应的物理页号,然后加上虚拟地址后半部分的偏移地址,就得到了物理地址,然后就可以根据该地址去访问相应内存,将数据返回给cpu。

关于这部分我的理解比较浅显,对于多级索引,TLB,都不是非常理解书中的表述方式。

1.4 Linux虚拟内存

Linux中每个进程的虚拟地址空间的结构都是相同的。将其分成两大部分,一个是用户地址空间,一个是内核地址空间。
用户地址空间就是我们之前一直研究的:代码段、数据段、堆、共享库、栈。
内核地址空间又可以分成两部分,一部分是每个进程不同的,包括进程的内核栈等,另一部分是每个进程都相同的,包括一些全局变量等,这部分的虚拟地址通常被映射到相同的物理内存中。

用户地址空间的组织,是用过内核中的一些数据结构来表现的,这些数据结构标识了每个区域例如堆和栈的具体位置,以及这些区域的访问权限等。

2. 自己的感悟

进程在运行的时候,直接使用的所有地址,都是虚拟地址,这个虚拟地址空间可能非常大,远比物理内存要大,也就是说虚拟地址的位数就可能比物理地址的位数要多得多,这个也很容易理解,毕竟虚拟地址其实是为了表示磁盘的,磁盘当然要比物理内存要大得多。而操作系统会自动将虚拟地址映射成物理地址,仿佛进程可以访问很大的地址空间一样。当然这个虚拟地址得是64位范围之内,否则就无法表示了,而2的64次方是一个非常非常非常大的数字,所以我们不用担心虚拟地址应该多大的问题,你想要多大就可以是多大。

链接器在生成完全链接的可执行文件之后,并不意味着可执行文件中的地址都是物理地址,相反,其中所有的地址都是虚拟地址,我们不能把链接器的重定位和虚拟内存系统的地址翻译搞混淆了。

重定位之后的所谓“可执行文件”,其实还是建立在虚拟内存地址翻译的基础上的。

所以说,虚拟内存真的是默默在工作。我们的程序在编译、连接、加载的时候,几乎都不看虚拟内存,所谓的不看,是因为虚拟内存都做好了,我们不用去关心程序实际在机器中的地址,这也就是前面说的所谓虚拟内存方便内存管理的原因。

3. 明日目标

  1. LeetCode两道题
  2. C++primer看一点
  3. 完成虚拟内存后面部分,看不懂就跳过。以后再回来看好了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值