C++面试总结之操作系统(二):内存管理

1.伙伴系统 SLAB算法

伙伴系统,其思想是:把内存块分成不同的组(1,2,4,8,16,32....);分配内存时找到能够满足条件 的最小的块;如果找不到,就找大的块,然后一分为 2,分配一块,留一块;回收时:如果有相邻的同样大小的块,则合并。

Malloc 实现原理:可以基于伙伴系统实现,也可以使用基于链表的实现

• 将所有空闲内存块连成链表,每个节点记录空闲内存块的地址、大小等信息

• 分配内存时,找到大小合适的块,切成两份,一分给用户,一份放回空闲链表 

•free 时,直接把内存块返回链表

解决外部碎片:将能够合并的内存块进行合并

2.什么是缓冲区溢出?有什么危害?其原因是什么? 

缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。 

危害有以下两点:

(1)程序崩溃,导致拒绝额服务

(2)跳转并且执行一段恶意代码

造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。缓冲区攻击的最终目的就是希望系统能执行这块可读写内存中已经被蓄意设定好的恶意代码。

3.分页和分段有什么区别?

分段:将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。

分页:用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。

(1)页是信息的物理单位,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要。段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现存储保护和信息的共享,满足用户的需要。

(2)页的大小固定,由系统确定,将逻辑地址划分为页号和页内地址是由机器硬件实现的.而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时根据信息的性质来划分.

(3)分页的作业地址空间是一维的.分段的地址空间是二维的. 

4.使用 mmap 读写文件为什么比普通读写函数要快?

像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据: 一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,共享内存中的内容往往是在解除映射时才写回文件的。

UNIX访问文件的传统方法是用open打开它们, 如果有多个进程访问同一个文件, 则每一个进程在自己的地址空间都包含有该文件的副本,这不必要地浪费了存储空间. 下图说明了两个进程同时读一个文件的同一页的情形. 系统要将该页从磁盘读到高速缓冲区中, 每个进程再执行一个存储器内的复制操作将数据从高速缓冲区读到自己的地址空间.

共享存储映射:进程A和进程B都将该页映射到自己的地址空间, 当进程A第一次访问该页中的数据时, 它生成一个缺页中断. 内核此时读入这一页到内存并更新页表使之指向它.以后, 当进程B访问同一页面而出现缺页中断时, 该页已经在内存, 内核只需要将进程B的页表登记项指向此页即可. 如下图所示: 

(1)mmap()及其相关系统调用

mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset ) 

mmap的作用是映射文件描述符fd指定文件的 [off,off + len]区域至调用进程的[addr, addr + len]的内存区域, 如下图所示:

(2)对mmap()返回地址的访问

linux采用的是页式管理机制。对于用mmap()映射普通文件来说,进程会在自己的地址空间新增一块空间,空间大小由mmap()的len参数指定,注意,进程并不一定能够对全部新增空间都能进行有效访问。进程能够访问的有效地址大小取决于文件被映射部分的大小。简单的说,能够容纳文件被映射部分大小的最少页面个数决定了进程从mmap()返回的地址开始,能够有效访问的地址空间大小。超过这个空间大小,内核会根据超过的严重程度返回发送不同的信号给进程。可用如下图示说明:

总结一下就是, 文件大小, mmap的参数 len 都不能决定进程能访问的大小, 而是容纳文件被映射部分的最小页面数决定进程能访问的大小。

5.缓存处理的算法有哪些(9个),讲一讲LRU算法

常用的页面置换算法:

(1)最常不使用法:LFU(踢掉频率最小的)

(2)最近最少使用置换法(LRU):当需要置换一页时,选择在最近一段时间里最久没有使用过的页面予以淘汰

浏览器就是使用了LRU作为缓存算法。

        最近最少使用 twice(LRU2):把被两次访问过的对象放入缓存池,当缓存池满了之后,我会把有两次最少使用的缓存对象踢走。因为需要跟踪对象2次,访问负载就会随着缓存池的增加而增加。

        Two Queues(2Q):把被访问的数据放到 LRU 的缓存中,如果这个对象再一次被访问,我就把他转移到第二个、更大的 LRU 缓存。踢走缓存对象是为了保持第一个缓存池是第二个缓存池的1/3。

(3)Adaptive Replacement Cache(ARC)(性能最好,可自调,低负载):

由2个 LRU 组成,第一个,也就是 L1,包含的条目是最近只被使用过一次的,而第二个 LRU,也就是 L2,包含的是最近被使用过两次的条目。

(4)最近最常使用置换Most Recently Used(MRU):

常用于数据库内存缓存中,每当一次缓存记录的使用,我会把它放到栈的顶端。当栈满了的时候,把栈顶的对象给换成新进来的对象!

(5)最近未使用置换法(NUR):是LRU算法的近似方法,选择在最近一段时间里未被访问过的页面予以淘汰

(6)先进先出法:(置换次数比较多)

(7)Second Chance:像FIFO 一样在观察队列的前端,但是很FIFO的立刻踢出不同,我会检查即将要被踢出的对象有没有之前被使用过的标志(1一个 bit 表示),没有被使用过,我就把他踢出;否则,我会把这个标志位清除,然后把这个缓存对象当做新增缓存对象加入队列。

(8)CLock:有一个装有缓存对象的环形列表,头指针指向列表中最老的缓存对象。当缓存 miss 发生并且没有新的缓存空间时,我会问问指针指向的缓存对象的标志位去决定我应该怎么做。如果标志是0,我会直接用新的缓存对象替代这个缓存对象;如果标志位是1,我会把头指针递增,然后重复这个过程,知道新的缓存对象能够被放入。

(9)最佳置换法(OPT):选择将来不再使用或在最远的将来才被访问的页调换出去(不便于实现)

6.缓存memrycache、缓存的作用

缓存的三大作用,就是预读取(预先读取将要载入的数据)、存储临时访问过的数据和对写入的数据进行暂时存放。让缓存夹在CPU与内存之间,CPU缓存的存在,主要是为了解决内存的“慢速”与CPU的"快速"之间的矛盾。

7.inode节点,文件名是否存放在inode节点里

目录项中包含了文件名和i节点

一个目录文件包含了一组目录项,目录项是放在data block中的。

一个目录项主要包括了文件名和索引节点号,索引节点号是指向索引节点表( system inode table )中对应的索引节点的。 或者这样解释一下目录项,因为目录可以包含子目录,目录是可以层层嵌套的,所以形成文件路径,而文件路径中的每一部分就是所谓的目录项(dentry)。

注:内核后来的版本采用ext文件系统时,目录项中就不是存放索引节点号,然后找到索引节点表,再找到索引节点了。而是目录项中存放文件名和一个指向索引节点的指针。

文件系统处理文件所需要的所有信息都存放在称为索引节点的数据结构中。inode包含文件的元信息,具体来说有以下内容:(!!!!不包含文件名)

  * 文件的字节数
  * 文件拥有者的User ID
  * 文件的Group ID
  * 文件的读、写、执行权限
  * 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
  * 链接数,即有多少文件名指向这个inode
  * 文件数据block的位置

可以用stat命令,查看某个文件的inode信息:

stat example.txt

查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。

df -i

查看每个inode节点的大小,可以用如下命令:

sudo dumpe2fs -h /dev/hda | grep "Inode size"

使用ls -i命令,可以看到文件名对应的inode号码:

ls -i example.txt

每个inode都有一个号码,操作系统用inode号码来识别不同的文件。Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。

8. 硬链接与软连接

(1)硬链接

Unix/Linux系统允许,多个文件名指向同一个inode号码。这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为"硬链接"(hard link)。

ln命令可以创建硬链接:

ln 源文件 目标文件

目录文件的"链接数"。创建目录时,默认会生成两个目录项:"."和".."。前者的inode号码就是当前目录的inode号码,等同于当前目录的"硬链接";后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的"硬链接"。所以,任何一个目录的"硬链接"总数,总是等于2加上它的子目录总数(含隐藏目录),这里的2是父目录对其的“硬链接”和当前目录下的".硬链接“。

(2)软链接

文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的"软链接"(soft link)或者"符号链接(symbolic link)。

这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:"No such file or directory"。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode"链接数"不会因此发生变化。

ln -s命令可以创建软链接。

ln -s 源文文件或目录 目标文件或目录

猜你喜欢

转载自blog.csdn.net/lxin_liu/article/details/89317107