`
xitong
  • 浏览: 6198357 次
文章分类
社区版块
存档分类
最新评论

读《linux开发工具箱》的一点感想

 
阅读更多

今天周六,又是清明节,我起床很晚,原因不是我懒,是因为我在被窝里看书了,看的是《linux开发工具箱》,书是老外写的,被翻译成了中文,可以说翻译的很差劲,很多地方感觉不通顺,于是我吭吭哧哧半天过不去一句话,好在我的linux基础比较好,读此书不是为了获取什么知识,而是为了得到一种截然不同的表达或者理解方式罢了,书的整体结构还是比较明朗的,今天主要读了第五章的第6节,讲的是“用户空间的内存管理”,本文就把这一章的收获理一下。

首先说的就是虚拟内存,说到虚拟内存,我在前面的文章也说了,其实就是为了平衡确定和不确定之间的差异,确定的物理内存,不确定的是进程行为以及进程的数量,如果说每个进程占用大小为n的内存,那么物理内存为M最多可以同时运行M/n个进程,其它的进程就必须等待这一轮的进程有一个换出或者执行完毕或者阻塞之后才可以换进一个,这样的后果是,同时运行的进程页面不会被换出,可以永驻内存,可是一旦由于阻塞或者时间片到期而换出内存就需要等待很长时间,进程一下子必须将其地址空间全部换出,然后轮到它执行时又要全部换入,这是一种很不好的情况,原因就在于,我们没有必要让一个进程执行时其地址空间全部在内存,因为毕竟cpu一次只能访问一个地址,同时又不能忍受一个进程为等待运行而等待太久,进程越多,进程的平均等待时间越久,于是这种粗粒度的换入换出方式必须改善。进程大小以及进程数量的不确定是个不可改变的事实,但是可是改变的是进程使用内存的方式,如果说不再是一个进程一个进程的换入换出,而是一个页面一个页面的换入换出,那么就需要一套硬件机制和软件策略来映射页面和它的属主进程,而且还要处理好共享关系。于是现在的页式MMU体系就出现了,粒度很细,基于页面,当然可以再细,但是那可能就会导致另外的问题。这里就不再说cpu cacheline和tlb了。如此一来,新的虚拟内存体系建立了,所有的进程都有自己独享的4G内存(32位地址总线),进程的大环境都相同了,不像以前那样,每个进程的地址空间不同,现在mmu可以用统一的方式管理每个进程的4G空间了,管理更加高效。请求换页的一个缺点就是不再是基于进程的全局换入换出了,这就造成了页面置换的抖动,消除抖动的方式之一就是工作集,其实就是确保每个进程总会有一些页面不被换出,而这些页面就是最近经常被访问的页面。

这里我想到的是linux操作系统内核的进程调度的粒度,原来的O(1)式的调度算法,现在时cfs的算法,前者更类似于原始的进程交换的内存管理,而后者的粒度更细,更像是页式管理,在O(1)算法中,虽然运行中的进程调度很有效,但是一旦到了过期队列就要长等,为了避免这种长等,就必然引入了一系列的预测算法和硬限制条件,比如不能长等超过一个时间等等,这样的后果就是算法及其复杂,最终还是会有一些抖动现象,而cfs就不错,抛弃了很多复杂的算法,细粒度可以做到完全公平。细粒度做到平均就是比粗粒度更加容易,做个试验,手里拿n个大小不同的球,放入一个容器使劲摇,最后看看是否比较均匀,然后把这些球磨成粉,再摇,再看看,不要说这样不可行,想想看,看它们的都是我们人。

接下来我比较感兴趣的就是内存耗尽,书中用了很大的篇幅举个好几个例子来说明这一点。其实内存耗尽是一个很明显的现象,物理内存就那么大,如果不注意,总会耗尽的。内存耗尽有两种耗尽,一种是虚拟内存耗尽,另一种是物理内存耗尽,虚拟内存耗尽比较好理解,因为系统给进程的编程空间就是3G(linux大部分内核上)在编程的时候就不能耗尽这个大小的空间,当然没有哪个程序员会知道自己用了多大的内存,那么等到运行时会被检测出来,其实虚拟空间耗尽就是在分配内存时发生的,而不是在访问的时候,因为都是先分配虚拟内存,然后在访问的时候再分配物理内存。

虚拟内存在linux中很多情况下一共有3G,应用程序可以随意使用,可是glibc不太信任大多数的程序员,因此它实现了自己的管理方式,主要的动态内存管理方式就是堆内存和更大的用mmap分配的内存,glibc通过查看malloc的参数,如果超过一个限定值,那么就不再从堆中分配,而是直接用mmap来分配,其实堆这种方式是从老的系统中继承下来的,在mm_struct中就是brk字段,brk系统调用可以扩展堆的空间,malloc分配小内存的时候,将直接使用glibc的堆管理器来在堆中分配,一旦池中没有空闲块,那么就用brk从操作系统中分配,其实brk也是用mmap来分配的,只是它的起始地址参数是mm的brk字段,而直接用mmap调用映射的内存的起始地址参数却是可以在珍整个虚拟地址空间查找的,而不管这块虚拟地址在何方,这儿意义上,malloc可以从堆的池中分配,也可以用mmap直接分配,并且,brk只是一个mmap实现的一个包装,只不过这个brk的实现在插入vma的时候必须用固定的地址罢了。

关于锁定内存,linux提供了mlock和mlockall函数,后者可以将该进程的现有内存或者将来的内存锁定在内存中,怎么锁定未来的内存呢?就是通过在该进程的mm_struct中设置一个默认标志,任何的mmap操作的标志都需要或上这个默认标志,在调用mlockall来锁定未来内存的时候,只需要将这个默认标志或上一个lock标志就可以了,很简单吧。简单是简单,关键是很艺术。这个章节接下来的内容是进程的资源限制,其实就是set/getrlimit系统调用,这个系统调用就不多说了,这是linux的基本的进程管理限制机制,不过要说的是,在2.6.25后,实现了cgroup这种更加好的容器化的资源控制机制,更好的实现虚拟化而且可以让不同的控制实体之间的联系更加紧密。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics