Linux内核设计与实现 读书笔记 转

时间:2022-03-26 09:08:21

Linux内核设计与实现  读书笔记:

http://www.cnblogs.com/wang_yb/tag/linux-kernel/

《深入理解LINUX内存管理》

http://blog.csdn.net/yrj/article/category/718110

第一篇 内存的测量

2.1. 系统当前可用内存

  1. # cat /proc/meminfo
    MemTotal:        8063544 kB
    MemFree:          900952 kB
    Buffers:         1183596 kB
    Cached:          1596808 kB

MemTotal:总共可用物理内存

Buffers:主要是用来给 Linux 系统中块设备做缓冲区

Cached:用来缓冲我们所打开的文件(Linux 的思想是,如果内存充足,不用白不用,它会使用内存来 cache 一些文件,从而加快进程的运行速度;当内存不足时,这些内存又会被回收,供程序使用。)

所以真正可用的内存 = MemFree + Buffers + Cached

被用掉的物理内存 = MemTotal - 真正可用的内存

  1. # ls /proc
  2. 1 1139 1536 1832 2042 2089 2151 2257 2647 313 4 4407 63 901 bus iomem modules sysrq-trigger
  3. 10 12 1541 1833 2043 2090 2155 22806 26820 32 40 47 6996 902 cgroups ioports mounts sysvipc
  4. 1003 1263 1661 1861 2045 2098 2172 22961 27 323 41 48 7 932 cmdline irq mtrr timer_list
  5. # ls /proc/1139
  6. attr        cmdline          environ  io        maps       mountstats  oom_score    sched      stack   syscall
    auxv        coredump_filter  exe      latency   mem        net         pagemap      schedstat  stat    task
    cgroup      cpuset           fd       limits    mountinfo  numa_maps   personality  sessionid  statm   wchan
    clear_refs  cwd              fdinfo   loginuid  mounts     oom_adj     root         smaps      status
  7. # cat status

数字代表着一个个目录;同时这些数字也与当前系统中运行进程的 PID 一一对应。在这些目录下面的文件,记录着这些进程在 Linux 内核中相应的数据。其中status记录了一些比较有用的信息,而oom_adj是OOM机制的重要参考。

2.2. 虚拟内存与物理内存

malloc只是分配了虚拟内存,kernel 不会分配物理页面给进程。
strcpy一类操作时,进程需要使用这块内存了,kernel 会产生一个页故障,从而为系统分配一个物理页面。
kernel 分配物理内存的最小单位为一个物理页面,一个物理页面为(4K Byte)

  1. # cat statm
  2. 345 87 74 1 0 58 0

这里有 7 个数,它们以页(4K)为单位。
Size (total pages,345) 任务虚拟地址空间的大小
Resident(pages,87) 应用程序正在使用的物理内存的大小
Shared(pages,74) 共享页数
Trs(pages,1) 程序所拥有的可执行虚拟内存的大小
Lrs(pages,0) 被映像到任务的虚拟内存空间的库的大小
Drs(pages,58) 程序数据段和用户态的栈的大小
dt(pages,0) 脏页数量(已经修改的物理页面),这个数一些系统可能修改成直接返回0了
其中Size Trs Lrs Drs对应于进程的虚拟内存,Resident shared dr对应于物理内存

  1. #cat maps
  2. 00008000-00009000 r-xp 00000000 1f:12 288 /mnt/msc_int0/hello // 该段内存地址对应于进程的代码段,4k
  3. 00010000-00011000 rw-p 00000000 1f:12 288 /mnt/msc_int0/hello // 该段内存地址对应与进程的数据段,4k
  4. 00011000-00032000 rwxp 00011000 00:00 0                       // heap,132k
  5. 40000000-40002000 rw-p 40000000 00:00 0                       //
  6. 41000000-41017000 r-xp 00000000 1f:0d 817360 /lib/ld-2.3.3.so
  7. 4101e000-41020000 rw-p 00016000 1f:0d 817360 /lib/ld-2.3.3.so
  8. 41028000-41120000 r-xp 00000000 1f:0d 817593 /lib/libc-2.3.3.so
  9. 41120000-41128000 ---p 000f8000 1f:0d 817593 /lib/libc-2.3.3.so
  10. 41128000-41129000 r--p 000f8000 1f:0d 817593 /lib/libc-2.3.3.so
  11. 41129000-4112c000 rw-p 000f9000 1f:0d 817593 /lib/libc-2.3.3.so
  12. 4112c000-4112e000 rw-p 4112c000 00:00 0
  13. befeb000-bf000000 rwxp befeb000 00:00 0                        // stack, 84K

00008000-00009000 r-xp 00000000 1f:12 288 /mnt/msc_int0/hello分别对应:

内存段虚拟地址起始,权限,偏移量,映射文件的主设备号和次设备号,映射文件节点号,映像文件的路径。

我们可以通过 cat /proc/devices 来查看指定映射文件的主设备号的设备信息。

32位操作系统每个进程4G虚拟内存,由上图可知:0x00000000-0xbfffffff,用户空间3G;0xc0000000-0xffffffff,内核空间1G

实际物理内存如下:

  1. #cat memmap                         // 这个命令不一定每个linux版本都有,没有的话可能要自己写驱动
  2. 2                                   // 对应cat maps第一行,也就是进程的代码段
    1                                   // 对应cat maps第二行,也就是进程的数据段
    100000000000000000000000000000000   // 33个字符,对应132k内存,仅第一页实际使用了
    11
    4949494949494949491649494949494949494949494949
    11
    4917171717111617171717171717174949 0 049 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 049 0 0 0
    0 0 0 0 0 0 0 044 042424242 0 0 0 041 0 0 0 0 0 0 0 0 0 0 0 0 0 04847 0 0 0 0 0 0 0
    04747484949494949494949 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 049 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 04849 0 0 0 04849 0 0 047 049 0 0 0 0 0 0 0 0 0 0 04949 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 046 0 0 0 0 0 0 0 0 0 0 0 0 0 044 0
    000000000
    00000000
    49
    111
    11
    000000000000000000011

每个数字对应内存的一个页面(4k),如果为0,表示该页面空闲。其中代码段系统共享,数据段,堆栈是每个进程私有的。

2.3 关于交换分区和内存回收
之所以移动设备不采用交换分区:
1)交换分区会导致整体性能会下降很多
2)嵌入式设备一般使用 Flash 做为存储介质, Flash 写的次数是有限的。而如果在 Flash 上面建立交换分区的话,必然导致对 Flash 的频繁写,进而影响 Flash 的寿命
内存回收机制:
在 Linux 物理内存,每个页面有一个 dirty 的标志,如果该页面被改写了,我们称之为 dirty page。非dirty page都可以回收。也就是说,一个进程的代码段全部可以回收,数据段视情况而定,堆栈实际分配的物理内存均不能回收。

第二章 内存的优化

2.4 进程
一个进程运行时,所占的内存除了堆栈外,还包括:
全局变量、静态变量:初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。(数据段)
文字常量:常量字符串就是放在这里的,程序结束后由系统释放。(代码段)
程序代码:存放函数体的二进制代码。(代码段)

堆段:
malloc、free、new、delete 是我们从程序的角度去看待堆内存;而在 Linux 内核中会专门为进程分配一段内存地址,用来存放堆的内容;随着进程申请内存的增加,进程会通过系统调用brk,来让Linux内核扩展这段内存空间,从而分配给进程更多的内存;当进程释放内存时,进程又会通过系统调用brk,来告诉内核缩减这段内存空间,Linux 内核便会将其一部分物理内存进行回收。
由于用户态进程申请内存是以字节为单位,而在内核中内存的管理是以页面(4K)为单位;如何减少brk次数成为提升系统性能的一个关键。
堆内分配内存最少为16个字节,以8位对齐,也就是说16位,24位,32位,以此类推。

char* p;
p = malloc(20);
那么,实际分配的大小会写在:*(p-4),具体内存分布为:
*(p-5): prev_size
*(p-4): size
*(p-3): 标志位
*(p-2): M值。M=1表示该内存块通过mmap来分配,只有在分配大块内存时,才采用mmap的方式,那么在释放时会的munmap_chunk()去释放;否则,释放时由chunk_free()完成。实际上在glibc的内存管理中,采用brk的方式,只能管理1G 地址空间以下的内存,如果大于了1G,glibc 将采用mmap的方式,为堆申请一块内存。brk用于更改堆顶地址,而mmap则为进程分配一块虚拟地址空间。
*(p-1): P值。P=0,表示上一块空闲,这时prev_size通常为上一块的大小;P=1,表示上一块正在被使用,这时prev_size通常为0。

为了提高速度,glibc的malloc实现了fastbins,可以通过mallopt(M_MXFAST, value)来设置其伐值。对于所有小于M_MXFAST阀值的小块内存释放时,不会去尝试合并,还会被复用;大于M_MXFAST的会尝试合并和复用。M_MXFAST阀值设为0相当于禁用fastbins功能,也就是说会一直尝试合并和复用内存。总体来说,M_MXFAST对于内存使用的影响没有一个定论,但它肯定会加快进行的运行速度。

大块内存分配:
大块内存分配以后,通过查看 maps 文件,你可以发现增加一段线性区(也就是多了一行)。其权限为 rw-p。与进程缺省堆内存的权限rwxp是不同的。
一些阀值的设置方式也是调用mallopt。取值分别为M_MMAP_THRESHOLD、M_MMAP_MAX。
M_MMAP_THRESHOLD
libc中大块内存阀值,大于该阀值的内存申请,内存管理器将使用mmap系统调用申请内存;如果小于该阀值的内存申请,内存管理其使用brk系统调用来扩展堆顶指针。该阀值缺省值为128kB。
M_MMAP_MAX
该进程中最多使用mmap分配地址段的数量。设为0表示该进程禁用mmap功能。默认值为65536。

内存释放:
在libc中,只有当堆顶有连续的128k空闲内存时,libc才会掉用brk,来通知内核释放这段内存,将其返还给系统。这个默认的128k触发条件可以通过mallopt修改M_TRIM_THRESHOLD来改变。
M_TRIM_THRESHOLD
堆顶内存回收阀值,当堆顶连续空闲内存数量大于该阀值时,libc的内存管理其将调用系统调用brk,来调整堆顶地址,释放内存。该值缺省为128k。
M_TOP_PAD
该参数决定了,当libc内存管理器调用brk释放内存时,堆顶还需要保留的空闲内存数量。该值缺省为 0.

内存空洞:
其实是由于linux内存分配释放机制导致的。只要堆顶没释放,下面的释放将不会实质上真正释放。
应对策略:1)调低mmap阀值,用mmap,代价是,会多次调用系统调用(mmap和unmmap),使进程整体性能下降。
               2)尽量优先释放堆顶内存。代码习惯问题,比较难以彻底解决。
内存空洞和内存泄漏是有区别的,后者会一直增加,前者增加到一定地步趋于稳定。

2.4 内存的跟踪
使用 mtrace 检测内存泄漏
1、 引入头文件 #include 
2、 在需要跟踪的程序中需要包含头文件,而且在main()函数的最开始包含一个函数调用:mtrace()。由于在main 函数的最开头调用了mtrace(),所以该进程后面的一切分配和释放内存的操作都可以由mtrace来跟踪和分析。
3、 在运行进程前定义一个环境变量,用来指示一个文件。该文件用来输出 log 信息。如下的例子:
$ export MALLOC_TRACE=mymemory.log
4、 正常运行程序。此时程序中的关于内存分配和释放的操作都可以记录下来。

Linux内核设计与实现 读书笔记 转的更多相关文章

  1. Linux内核设计与实现 读书笔记

    第三章 进程管理 1. fork系统调用从内核返回两次: 一次返回到子进程,一次返回到父进程 2. task_struct结构是用slab分配器分配的,2.6以前的是放在内核栈的栈底的:所有进程的ta ...

  2. Linux内核设计与实现读书笔记(8)-内核同步方法【转】

    转自:http://blog.chinaunix.net/uid-10469829-id-2953001.html 1.原子操作可以保证指令以原子的方式执行——执行过程不被打断.内核提供了两组原子操作 ...

  3. Linux内核设计与实现——读书笔记2:进程管理

    1.进程: (1)处于执行期的程序,但不止是代码,还包括各种程序运行时所需的资源,实际上进程是正在执行的 程序的实时结果. (2)程序的本身并不是进程,进程是处于执行期的程序及其相关资源的总称. (3 ...

  4. Linux内核设计与实现——读书笔记1:内核简介

    内核:有的时候被称管理者或者操作系统核心,通常内核负责响应中断的中断服务程序, 负责管理多个进程从而分享处理器时间的调度程序,负责管理进程地址空间德内存管理程序 和网络,进程间通信等系统服务程序共同组 ...

  5. 初探内核之《Linux内核设计与实现》笔记上

    内核简介  本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核   原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单.2. 高效 ...

  6. Linux内核架构与底层--读书笔记

    linux中管道符"|"的作用 命令格式:命令A|命令B,即命令1的正确输出作为命令B的操作对象(下图应用别人的图片) 1. 例如: ps aux | grep "tes ...

  7. Linux内核设计第十七章笔记

    第十七章 设备与模块 关于设备驱动和设备管理,四种内核成分 设备类型:在所有unix系统中为了统一普通设备的操作所采用的分类 模块:Linux内核中用于按需加载和卸载目标代码的机制 内核对象:内核数据 ...

  8. Linux内核分析 一二章读书笔记

    第一章 Linux内核简介 1.Unix (1)Unix系统很简洁 (2)在Unix中,所以东西都被当作文件对待,通过一套相同的系统调用接口来进行:open(),read(),write(),lsee ...

  9. 《Linux内核设计与实现》笔记-1-linux内核简单介绍

    一.Linux内核相对于传统的UNIX内核的比較: (1):Linux支持动态内核模块. 虽然Linux内核也是总体式结构,但是同意在须要的时候动态哦卸除(rmmod xxx)和载入内核模块(insm ...

随机推荐

  1. 字符串操作 replace

    "hello".charAt(0); // "h" "hello, world".replace("hello", &q ...

  2. require.js 入门笔记

    网站越来越庞大,JS也是越写越多. 当所有的JS 都集中在 HTML的 head 部分时,网页加载变得很慢,很多的 JS代码也并不是全都适用在当前的页面,造成了代码的冗余度非常高. 而且长长的JS代码 ...

  3. java中HashMap详解

    HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实 ...

  4. js中关于arguments

  5. POJ 3304 Segments (直线和线段相交判断)

    Segments Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7739   Accepted: 2316 Descript ...

  6. 5.5 Function类型

    说起来ECMAScript中上面最有意思,我想那莫过于函数了,有意思的根源,则在于函数实际上是对象.每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法.由于函数是对象,因此 ...

  7. 你必知必会的SQL面试题

    写在前面的话 本文参考原博<走向面试之数据库基础:一.你必知必会的SQL语句练习-Part 1>和<走向面试之数据库基础:一.你必知必会的SQL语句练习-Part 2>进行练习 ...

  8. &OpenCurlyDoubleQuote;浅入浅出”函数防抖(debounce)与节流&lpar;throttle&rpar;

    函数防抖与节流是日常开发中经常用到的技巧,也是前端面试中的常客,但是发现自己工作一年多了,要么直接复用已有的代码或工具,要么抄袭<JS高级程序设计>书中所述"函数节流" ...

  9. lumion实例渲染6&period;2

    放置一些树 打开室外材质库,选择一个·别墅 放车 “景观”“描绘系统”笔刷调大刷出地面, 人群安置一些墙 改变方向,增大数量,墙就连在一起, 放一些树篱在墙上. 拍照模式添加特效,太阳 添加特效,天气 ...

  10. 【转】Java学习---集合框架那些事

    [原文]https://www.toutiao.com/i6593220692525711885/ Arraylist 与 LinkedList 异同 1. 是否保证线程安全: ArrayList 和 ...