系统内存和进程内存

===系统内存===

系统内存的使用情况可以用以下公式表示:
MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X】+【Active + Inactive + Unevictable + (HugePages_Total * Hugepagesize)】
MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X】+【Cached + AnonPages + Buffers + (HugePages_Total * Hugepagesize)】
MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X】+【ΣPss + (Cached – mapped) + Buffers + (HugePages_Total * Hugepagesize)】

File-backed内存,anon匿名内存,Shmem是tmpfs所使用的内存

=cached是和file有关的所有文件映射和文件系统
【buffers + cached 】= 【Active(file) + Inactive(file) + Shmem 】

=AnonPages不算tmpfs,shmem,纯进程申请malloc和占用内存
【Active(anon)+Inactive(anon)+(mlock=Unevictable)】 = 【AnonPages + Shmem】

=ΣPss = grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {print total}'
【AnonPages】=【ΣPss – mapped】
mapped包括代码段和数据段

SHR是RES中”映射至文件”的物理内存总和。包括:
程序的代码段。
动态库的代码段。
通过mmap做的文件映射。
通过mmap做的匿名映射,但指明了MAP_SHARED属性。
通过shmget申请的共享内存。
/proc//smaps内Shared_*统计的是RES中映射数量>=2的物理内存。
/proc//smaps内Private_*统计的是RES中映射数量=1的物理内存。

===进程内存===

程序编译:预编译hello.i,编译hello.s,汇编hello.o,连接a.out
ELF文件一共有4种类型:Relocatable file、Executable file、Shared object file和Core Dump file
.o文件,目标文件只是ELF文件的可重定位文件(Relocatable file)
.so文件是Shared object file
bin可执行文件是Executable file

文件结构:text代码段,data数据段,bss预留段,other sections,(只读,注释,堆栈)
sectios table,string table,symbol table

1,ELF文件大小
    size:    输出指定输入文件各段及其总和的大小
    text       data        bss        dec        hex    filename
    1492        564         12       2068        814    sleep

ELF文件提供两种视图,使用objdump工具和readelf工具可以查看分析elf文件的格式
link:ELF header,program header table,sections,section header table
exec:ELF header,program header table,segments,section header table

- ELF header: 描述整个文件的组织。
- Program Header Table: 描述文件中的各种segments,用来告诉系统如何创建进程映像的。
- sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,
    也就是说,在链接阶段,我们可以忽略program header table来处理此文件,
    在运行阶段可以忽略section header table来处理此程序
    segments与sections是包含的关系,一个segment包含若干个section。
    当ELF文件被加载到内存中后,系统会将多个具有相同权限(flg值)section合并一个segment
    如果section的长度不是其整数倍,则导致多余部分也将占用一个页
- Section Header Table: 包含了文件各个segction的属性信息

对比三类ELF文件,我们得到了以下结论: 
(1)e_type标识了文件类型 
(2)Relocatable File(.o文件)不需要执行,因此e_entry字段为0,且没有Program Header Table等执行视图 
(3)不同类型的ELF文件的Section也有较大区别,比如只有Relocatable File有.strtab节


2,汇编程序
反汇编代码
查看源代码被翻译成的汇编代码, 大概有3种方法, 
1) 通过编译器直接从源文件生成, 如gcc -S 
2) 对目标代码反汇编, 一种是静态反汇编, 就是使用objdump
3) 另外一种就是对运行时的代码反汇编, 一般通过gdb disassemble
readelf并不提供反汇编功能. 
objdump -d,查看汇编

gdb主要功能的实现依赖于一个系统函数ptrace
gdb使用ptrace的基本流程
1、gdb调试一个新进程:通过fork函数创建一个新进程,
在子进程中执行ptrace(PTRACE_TRACEME, 0, 0, 0)函数,然后通过execv()调用准备调试的程序。
2、attach到已运行进程:将pid传递给gdb,然后执行ptrace(PTRACE_ATTACH, pid, 0, 0)

gdb 设置断点,向该地址写入断点指令INT3,即0xCC,发送SIGSTOP信号
gdb 单步调试,next指令时,会计算下一条语句对应的第一条指令的地址,然后控制目标程序走到该位置停止


3,看data和bss段内容
.data、.bss、.rodata都属于数据段。其中,
.data    存放已初始化的全局变量、常量、静态局部变量。
.bss    存放未初始化的全局变量,静态局部变量,所以此段数据均为0,仅作占位。
.rodata    是只读数据段,此段的数据不可修改,存放常量
static int x1 = 0;    bss段
static int x2 = 12;    data段

4,符号表是什么
查看ldd依赖项:objdump -x xxx.so | grep "NEEDED" 
查看动态符号表: objdump -T xxx.so
查看符号表: objdump -t xxx.so

 

===OOM===

echo 0 > /proc/sys/vm/zone_reclaim_mode:意味着关闭zone_reclaim模式,可以从其他zone或NUMA节点回收内存
echo m > /proc/sysrq-trigger
lowmem_reserve[]: 0 1951 96891 96891
[38905.295396] Node 0 DMA: 1*4kB 2*8kB 0*16kB 1*32kB 2*64kB 0*128kB 1*256kB 0*512kB 1*1024kB 1*2048kB 3*4096kB = 15796kB
lowmem_reserve[]: 0 0 94940 94940
[38905.295405] Node 0 DMA32: 130*4kB 65*8kB 75*16kB 72*32kB 95*64kB 22*128kB 10*256kB 7*512kB 4*1024kB 2*2048kB 86*4096kB = 380032kB
lowmem_reserve[]: 0 0 0 0
[38905.295414] Node 0 Normal: 12544*4kB 68*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 1*4096kB = 54816kB

order:1, mode:0x20 可以看出来是GFP_ATOMIC类型的申请,且order = 1(page = 2 )
free pages = 380032KB = 95008 pages < low(1000KB = 250 pages) +  lowmem_reserve[normal](94940) = 95190
对于normal自己内存来说,free pages = 56552 KB = 14138 pages,也不用考虑lowmem_reserve(0),
但这时还会考虑申请order(1),减去order 0的12544个page后只剩 14138 - 12544 = 1594,也小于 low / 2 = (48788KB=12197pages) / 2 = 6098 pages。
所以初次申请尝试失败,进入__alloc_pages_slowpath() 尝试进行更为积极一些的申请。

GFP_ATOMIC是直接返回,不会进行回收
direct reclaim会比较消耗时间的原因是,它在回收的时候不会去区分dirty page和clean page,
如果回收的是dirty page,就会触发磁盘IO的操作,它会首先把dirty page里面的内容给刷写到磁盘,
它一次性的会回收32个pages,再去把该page给放到freelist里。
open一个文件时,如果没有使用O_DIRECT这个flag,那就是File I/O, 所有对磁盘文件的访问都要经过内存,
内存会把这部分数据给缓存起来;但是如果使用了O_DIRECT这个flag,那就是Direct I/O, 它会绕过内存而去直接访问磁盘

    第二次内存申请尝试
__alloc_pages_slowpath()首先是通过 gfp_to_alloc_flags() 修改alloc_pages,设上更为强硬的标志位。
这块根据原来的GFP_ATOMIC会设上 ALLOC_WMARK_MIN | ALLOC_HARDER | ALLOC_HIGH。但注意的是不会设上 ALLOC_NO_WATERMARKS 标志位。这个标志位不再判断zone的水位限制,属于优先级最高的申请,可以动用所有的reserve内存,
但条件是(!in_interrupt() && ((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE)))),
即要求不能在中断上下文,且是正在进行回收(例如kswapd)或者正在退出的进程。

之后进入拿着新的alloc_pages重新进入get_page_from_pagelist() 尝试第二次申请,
虽然有了 ALLOC_HARDER和ALLOC_HIGH,但是不幸的是在3个zone的zone_watermark_ok检查中还是都无法通过,
例如对于DMA32:free pages = 380032KB = 95008 pages
因为设上了ALLOC_HIGH 所以会将得到的watermark[min]减半,即min = min/2 = 800K / 2 = 400K = 100pages
而又因为设上了ALLOC_HARDER,会再将min砍去1/4,即min = 3 * min / 4 = 100 pages * 3 / 4 = 75 pages
即便如此,min(75 pages) +  lowmem_reserve[normal](94940) = 95015,仍大于free pages,仍认为无法分配内存,
同理DMA也不不成功,而normal中 free pages里连续8K的页太少也无法满足分配

第二次失败后,由于没有ALLOC_NO_WATERMARK也不会进入__alloc_pages_high_priority 进行最高优先级的申请,
同时由于是GFP_ATOMIC类型的分配不能阻塞回收或者进入OOM,因此就以申请失败告终。
遇到此种情况可以适当调高 min_free_kbytes 使kswapd较早启动回收,使系统一直留有较多的空闲内存,
同时可以适度降低 lowmem_reserve_ratio(可选),使得内存不足的情况下(主要是normal zone)可以借用DMA32/DMA的内存救急(注意不能也不能过低)

===kdump====
kdump是一种先进的基于kexec的内核崩溃转储机制。当系统崩溃时,kdump使用kexec 启动到第二个内核。第二个内核通常叫做捕获内核,以很小内存启动以捕获转储镜像。
# 立即重新启动计算机
echo "b" > /proc/sysrq-trigger
 
# 立即关闭计算机
echo "o" > /proc/sysrq-trigger
 
# 导出内存分配的信息 (可以用/var/log/message 查看)
echo "m" > /proc/sysrq-trigger
 
# 导出当前CPU寄存器信息和标志位的信息
echo "p" > /proc/sysrq-trigger
 
# 导出线程状态信息
echo "t" > /proc/sysrq-trigger
 
# 故意让系统崩溃
echo "c" > /proc/sysrq-trigger
 
# 立即重新挂载所有的文件系统 
echo "s" > /proc/sysrq-trigger
 
# 立即重新挂载所有的文件系统为只读
echo "u" > /proc/sysrq-trigger

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值