什么是虚拟内存?
虚拟内存简称虚存,是计算机系统内存管理的一种技术。它是相对于物理内存而言的,可以理解为“假的”内存。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),允许程序员编写并运行比实际系统拥有的内存大得多的程序,这使得许多大型软件项目能够在具有有限内存资源的系统上实现。
好处:
- 扩大地址空间。无论是段式虚存,还是页式虚存,或是段页式虚存,寻址空间都比实存大。
- 内存保护。每个进程运行在各自的虚拟内存地址空间,互相不能干扰对方。另外,虚存还对特定的
内存地址提供写保护,可以防止代码或数据被恶意篡改 - 公平分配内存。采用了虚存之后,每个进程都相当于有同样大小的虚存空间
坏处:
- 虚存的管理需要建立很多数据结构,这些数据结构要占用额外的内存
- 虚拟地址到物理地址的转换,增加了指令的执行时间
- 页面的换入换出需要磁盘IO,这是很耗时间的
- 如果一页中只有一部分数据,会浪费内存
解释下内存碎片,内碎片,外碎片?
内存碎片是由于多次进行内存分配造成的,当进行内存分配时,内存格式一般为:(用户使用段)(空白段)(用户使用段),当空白段很小的时候,可能不能提供给用户足够多的空间,如夹在中间的空白段的大小为5,而用户需要的内存大小为6,这样会产生很多的间隙,造成使用效率下降,这些很小的空隙叫碎片。
内碎片:分配给程序的存储空间没有用完,有一部分是程序不使用,但其他程序也没法用的空间。内碎片是处于区域内部或页面内部的存储块,占有这些区域或页面的进程并不使用这个存储块,而在进程占有这块存储块时,系统无法利用它,直到进程释放它,或进程结束时,系统才有可能利用这个存储块。 通俗点就是系统分配给进程但进程不使用的空闲空间。
外碎片:空间太小,小到无法给任何程序分配(不属于任何进程)的存储空间。外部碎片是出于任何已分配区域或页面外部的空闲存储块,这些存储块的总和可以满足当前申请的长度要求,但是它们的地址不连续或其他原因,使得系统无法满足当前申请。 通俗点就是系统分配空间是有很多小的不连续的空闲存储块。
解释下虚拟地址、逻辑地址、线性地址、物理地址?
虚拟地址:是指由程序产生的由段选择符和段内偏移地址组成的地址。这两部分组成的地址并没有直接访问物理内存,而是通过分段地址的变换处理后才会对应到相应的物理内存地址
逻辑地址:是指由程序产生的段内偏移地址。
线性地址:虚拟地址到物理地址变换之间的中间层, 虚拟地址的段选择符在GDT中找到对应的段基地址加上段内偏移。如果没有开启分页的话这就是物理地址,否则还要到页表中进行查找。
物理地址:CPU外部地址总线上的寻址物理内存的地址信号。
虚拟内存的置换方式是怎么样的?
最佳置换,先进先出、最近最少用、时钟。
给你一个类,里面有static,virtual之类的,说一说这个类的内存分布?
1. static修饰符
1.static修饰成员变量
对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当做是类的成员,无论这个类被定义了多少个,静态数据成员都只有一份拷贝,为该类型的所有对象所共享(包括其派生类)。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新。因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以它不属于特定的类对象,在没有产生类对象前就可以使用。
2.static修饰成员函数
与普通的成员函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针。从这个意义上来说,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,只能调用其他的静态成员函数。static修饰的成员函数,在代码区分配内存。
2. C++继承和虚函数
C++多态分为静态多态和动态多态。静态多态是通过重载和模板技术实现,在编译的时候确定。动态多态通过虚函数和继承关系来实现,执行动态绑定,在运行的时候确定。
动态多态的条件:1.虚函数;2.一个基类的指针或引用指向派生类的对象。
基类指针在调用成员函数(虚函数)时,就会去查找该对象的虚函数表。虚函数表的地址在每个对象的首地址。查找该虚函数表中该函数的指针进行调用。在类设计的时候,虚函数表直接从基类也继承过来,如果覆盖了其中的某个虚函数,那么虚函数表的指针就会被替换,因此可以根据指针准确找到该调用哪个函数。也就是说派生类的函数直接覆盖了父类上的虚函数的指针。
3. virtual修饰符
如果该类是virutal继承而来的子类,则该类的虚函数表指针和该类其他成员一起存储。虚函数表指针指向只读数据段中的类虚函数表,虚函数表中存放着一个个函数指针,函数指针指向代码段中的具体函数。
假设临界区资源释放,如何保证只让一个线程获得临界区资源而不是都获得?
给临界资源添加互斥锁,可以保证临界区资源释放,只有一个线程获得属性。
操作系统中的缺页中断是什么?
malloc()
和mmap()
等内存分配函数,在分配时只是建立了进程虚拟地址空间,并没有分配虚拟内存对应的物理内存。当进程访问这些没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常。
缺页中断:在请求分页系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中。每当所要访问的页面不在内存是,会产生一次缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。
缺页本身是一种中断,与一般的中断一样,需要经过4个处理步骤:
- 保护CPU现场
- 分析中断原因
- 转入缺页中断处理程序进行处理
- 恢复CPU现场,继续执行
OS缺页置换算法如何实现的?
当访问一个内存中不存在的页,并且内存已满,则需要从内存中调出一个页或将数据送至磁盘对换区,替换一个页,这种现象叫做缺页置换。当前操作系统最常采用的缺页置换算法如下: 先进先出、最近最少使用。
系统调用是什么,你用过那些系统调用,和库函数有什么区别?
系统调用是通向操作系统本身的接口,是面向底层硬件的。通过系统调用,可以使得用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互,是操作系统留给应用程序的一个接口。
库函数(Library function)是把函数放到库里,供别人使用的一种方式。.方法是把一些常用到的函数编完放到一个文件里,供不同的人进行调用。一般放在.lib文件中。库函数调用则是面向应用开发的,库函数可分为两类,一类是C语言标准规定的库函数,一类是编译器特定的库函数。
系统调用是为了方便使用操作系统的接口,而库函数则是为了人们编程的方便。
库函数调用与系统无关,不同的系统,调用库函数,库函数会调用不同的底层函数实现,因此可移植性好。由于库函数是基于c库的,因此不能用于内核对于底层驱动设备的操作。
区别:
- 库函数是语言或应用程序的一部分,而系统调用是内核提供给应用程序的接口,属于系统的一部分
- 库函数在用户地址空间执行,系统调用是在内核地址空间执行,库函数运行时间属于用户时间,系统调用属于系统时间,库函数开销较小,系统调用开销较大
- 库函数是有缓冲的,系统调用是无缓冲的
- 系统调用依赖于平台,库函数并不依赖
为什么要有page cahe,操作系统怎么设计的page cache?
加快从磁盘读取文件的速率。page cache
中有一部分磁盘文件的缓存,因为从磁盘中读取文件比较慢,所以读取文件先去page cache
中去查找,如果命中,则不需要去磁盘中读取,大大加快读取速度。在Linux
内核中,文件的每个数据块最多只能对应一个 Page Cache
项,它通过两个数据结构来管理这些 Cache
项,一个是radix tree
,另一个是双向链表。Radix tree
是一种搜索树,Linux
内核利用这个数据结构来通过文件内偏移快速定位Cache
项 。**