Linux线程栈内存总结

落爺英雄遲暮 提交于 2020-02-20 17:57:42
  1. 何为线程栈空间泄露?
  2. 何为anon内存?
  3. 虚拟内存、物理内存、anon内存的联系
  4. anon与线程的联系
  5. glibc源码库线程创建与销毁anon关系
  6. 使用pmap分析虚拟地址空间以及anon内存

何为线程栈空间泄露?

【栈空间泄露】:简单了说就是,创建了线程,系统分配了内存,但是由于异常操作,没有把之前申请的内存还给操作系统,缓存在进程中,导致这块的内存被占用,系统无法分配内存给其他进程。
从用户角度,线程创建后,未设置线程为detach属性,pthread_detach()、pthread_join()销毁函数。
从内存映射角度,在进程中缓存一些无法利用的anon内存。
从系统栈空间分配角度,这些运行结束但是未销毁的线程,既不放在stack_used队列里,也不放在stack_cache,从而线程栈空间不会被调度到。

何为anon内存?

仅个人观点,在操作系统上运行进程,其实是经过两次映射。一次是用户程序到系统配置器分配的虚拟内存申请,一次是虚拟地址空间到物理空间地址的映射,如果是第二次映射建立前,在申请物理空间,未填充物理页内容,建立映射后,仅仅代表已经分配了内存anon,ps:物理空间最小管理单元是“页”。

在这里插入图片描述

在这里插入图片描述

虚拟内存

操作系统给用户程序的假象地址,与物理内存建立映射关系。

物理内存

类似物理存储器DRAM

anon内存

1.调用malloc、brk、mmap等调用申请成功的虚拟内存后,在访问虚拟内存地址时候,发生页中断,去申请物理内存的时候,未填充物理页内容(读取、写入磁盘)。
2.已经建立与物理内存的映射关系,但是内存不被使用,系统也没有回收
ps:
1.使用cat /proc/cpuinfo可以查看操作系统分配给进程的虚拟内存地址空间范围,如一体机:address sizes:39 bits physical, 48 bits virtual,也就是2的48次方(256T)。
2.每个进程独立拥有“虚拟”的128T虚拟空间,各个进程空间独立,每个进程只有映射了物理空间才能正常使用(需要数据的读写)。
3.用户程序(用户空间)->库函数(malloc、pthread_create等)->系统调用(brk、mmap)->内核空间物理内存分配(vmalloc()、 kmalloc)。
4.anon生命周期:申请、使用、换出和换入、销毁
申请:使用malloc、brk(小于128K)、mmap(>128K映射)出现匿名页面的情况有很多,64M (内存池)、2M(栈空间)、4k(栈保护防溢出空间)、以及内存碎片。
使用: 这两种方式分配的都是虚拟内存,第一次没有分配物理内存。写已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
换出和换入:数据的读写,有专门的算法操作。
销毁:不管是堆内存还是栈空间,申请和释放都是对应的,如果只有申请没有释放,就会导致这块内存不能再利用。

1.使用cat /proc/cpuinfo可以查看操作系统分配给进程的虚拟内存地址空间范围,如一体机:address sizes:39 bits physical, 48 bits virtual,也就是2的48次方(256T)。
2.每个进程独立拥有“虚拟”的128T虚拟空间,各个进程空间独立,每个进程只有映射了物理空间才能正常使用(需要数据的读写)。
3.用户程序(用户空间)->库函数(malloc、pthread_create等)->系统调用(brk、mmap)->内核空间物理内存分配(vmalloc()、 kmalloc)。
4.anon生命周期:申请、使用、换出和换入、销毁
申请:使用malloc、brk(小于128K)、mmap(>128K映射)出现匿名页面的情况有很多,64M (内存池)、2M(栈空间)、4k(栈保护防溢出空间)、以及内存碎片。
使用: 这两种方式分配的都是虚拟内存,第一次没有分配物理内存。写已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
换出和换入:数据的读写,有专门的算法操作。
销毁:不管是堆内存还是栈空间,申请和释放都是对应的,如果只有申请没有释放,就会导致这块内存不能再利用。

【申请】:
一体机进程中调用线程创建函数pthread_create,会使用mmap创建线程空间2M,默认是8M(下图),在映射虚拟内存空间时,会把当前线程信息写入栈空间,所以就这块/proc/pidof core/maps就有了名字类似,[stack:1003]。
【销毁】:
一体机进程中调用线程销毁函数pthread_join()/ pthread_detach() ,在缓存线程20个以内,从stack_used队列删除,加入stack_cache队列,以供再次使用,表现为[anon],当大于20后,会使用munmap清除之前申请的栈空间内存。

1.资源泄露一般表现为:
①动态内存分配漏释放,malloc系列函数导致进程空间虚拟地址被分配完,或者物理地址被分配完。
②文件FD泄露,导致进程空间文件句柄数达到最大值。
③线程栈空间泄露,导致进程空间虚拟地址被分配完,进程内保留很多线程栈(stack)空间,表现为anon。

1.栈空间的申请都是低地址映射的,表示为:[stack:1811],其中1811是线程号,每个栈空间大小2048KB,每个栈空间下面都有一个大小为4K的栈防溢出缓冲区。感兴趣的可以研究递归如何导致栈溢出的?也可以根据线程Id对应线程空间地址,研究哈gdb随机崩溃如何判断跨线程栈。
2.当glibc中缓存队列小于20的时候,销毁线程,实则缓存在队列里面,表现为[anon],这个根据pmap工具版本不同,显示可能有差异,建议使用cat /proc/pidof core/maps。
3.内存泄露直接是泄露的虚拟内存,间接泄露的是物理内存。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!