嵌入式Linux进程信息及内存布局
Linux进程信息及内存分布简介
一、linux进程信息
本文以thread进程为例,简单创建两个线程。
1、获取进程状态cat/proc/<pid>/status(海思平台和ST平台差不多)
Name: thread
State: S (sleeping)
Tgid: 1199
Pid: 1199
PPid: 1195
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 32
Groups: 0
VmPeak: 13156 kB
VmSize: 13156 kB
VmLck: 0 kB
VmHWM: 256 kB
VmRSS: 256 kB
VmData: 12448 kB
VmStk: 160 kB
VmExe: 544 kB
VmLib: 0 kB
VmPTE: 12 kB
VmSwap: 0 kB
Threads: 3
SigQ: 0/1223
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000004
SigCgt: 0000000180000000
CapInh: 0000000000000000
CapPrm: ffffffffffffffff
CapEff: ffffffffffffffff
CapBnd: ffffffffffffffff
Cpus_allowed: 1
Cpus_allowed_list: 0
voluntary_ctxt_switches: 5
Name: //进程名字
State: //任务状态, 运行/睡眠/僵死/
Tgid: //线程组号
Pid: //任务ID
PPid: //父进程ID
TracerPid: //接收跟踪该进程信号的进程ID号
FDSize: //文件描述符最大个数
Groups: //启动该进程用户所属的组ID
VmPeak: //进程地址空间的大小,进程运行过程中占用内存的峰值,代表占用的最大内存空间
VmSize: //虚拟地址空间大小(total_vm进程地址空间大小 -reserved_vm进程在预留的内存间物理页)
VmLck: //已经锁定的物理内存大小(loack_vm)
VmHWM: //应用程序使用物理内存大小峰值
VmRSS: //应用程序正在使用的物理内存大小
VmData: //程序数据段的大小(所占虚拟内存大小),存放了初始化数据(total_vm - shared_vm - stack_vm)
VmStk: //任务用户堆栈大小(stack_vm)
VmExe: //程序拥有的可执行虚拟内存大小,代码段,不包括任务使用的库(end_code_start_code)
VmLib: //被映像到任务的虚拟内存空间的库大小(exec_lib)
VmPTE: //该进程页表大小
Threads: //线程个数
SigQ: 0/1935 //待处理信号个数
SigPnd: 0000000000000000 //屏蔽位,存储了该线程的待处理信号
ShdPnd: 0000000000000000 //屏蔽位,存储了该线程组的待处理信号
SigBlk: 0000000000000000 //存放被阻塞的信号
SigIgn: 0000000000000001 //存放被忽略信号
SigCgt: 00000001e0001cfe //存放被俘获到的信号
CapInh: 0000000000000000 //能被当前进程执行的程序的继承能力
CapPrm: ffffffffffffffff //进程能够使用的能力
CapEff: ffffffffffffffff //进程有效能力
CapBnd: ffffffffffffffff
Cpus_allowed: 3
Cpus_allowed_list: 0-1
voluntary_ctxt_switches: 1125
nonvoluntary_ctxt_switches: 301
2、获取进程内存空间映射cat/proc/<pid>/maps(海思平台)
反应进程占用的内存区域,每行数据意思:
开始——结束 访问权限 偏移 主设备号:次设备号 i节点 文件
00008000-00090000r-xp 00000000 00:0f 1014855 /home/thread
00090000-00091000rw-p 00088000 00:0f 1014855 /home/thread
00091000-00095000 rw-p00000000 00:00 0
0137d000-0139f000rw-p 00000000 00:00 0 [heap]
2ab52000-2ab53000rw-p 00000000 00:00 0
2ab71000-2ab72000 ---p00000000 00:00 0
2ab72000-2b171000 rw-p00000000 00:00 0
2b218000-2b219000---p 00000000 00:00 0
2b219000-2b818000rw-p 00000000 00:00 0
7e973000-7e99a000 rw-p 00000000 00:00 0 [stack]
ffff0000-ffff1000 r-xp 00000000 00:000 [vectors]
3、获取进程内存空间映射cat/proc/<pid>/maps(ST平台)
00008000-00078000r-xp 00000000 00:13 1047568 /home/n7/thread
0007f000-00081000rw-p 0006f000 00:13 1047568 /home/n7/thread
00081000-00084000rw-p 00000000 00:00 0
01499000-014bb000rw-p 00000000 00:00 0 [heap]
75fe4000-75fe5000 ---p00000000 00:00 0
75fe5000-767e4000 rw-p00000000 00:00 0 [stack:437]
767e4000-767e5000---p 00000000 00:00 0
767e5000-76fe5000rw-p 00000000 00:00 0 [stack:436]
7e9f7000-7ea1e000 rw-p 00000000 00:00 0 [stack]
ffff0000-ffff1000 r-xp 00000000 00:000 [vectors]
4、进程maps解析
从maps可以得到进程的一个内存布局图,不同颜色代表程序不同段映射:
(1)代码段
00008000-00090000r-xp 00000000 00:0f 1014855 /home/thread
(2)数据和BSS段
00090000-00091000rw-p 00088000 00:0f 1014855 /home/thread
00091000-00095000rw-p 00000000 00:00 0
数据和bss段与代码段基本都是紧挨着的。
(3)堆heap
01499000-014bb000rw-p 00000000 00:00 0 [heap]
向上增长
(4)mmap区域
thread进程主要是创建了两个线程,每个线程的堆栈大小为6M。
2ab71000-2ab72000 ---p00000000 00:00 0 //4K
2ab72000-2b171000 rw-p00000000 00:00 0 //6M
2b218000-2b219000---p 00000000 00:00 0 //4K
2b219000-2b818000rw-p 00000000 00:00 0 //6M
A. 海思与ST平台对比发现创建线程时mmap增长方向刚好相反,见第四节。
B. 线程通过mmap方式映射,为啥每个线程都多一个4K,见第三节。
(5)stack区域
7e973000-7e99a000 rw-p 00000000 00:00 0 [stack]
位于地址高位,向下增长。
二、进程镜像信息与maps关系
使用objdump工具可以查找thread二进制文件的地址和符号信息,和maps内容对比发现基本是一致的。
1、代码段
arm-hisiv200-linux-objdump -t thread | grep"\.text"得到如下信息:
……
0004e140 g F .text 00000008__wmemcpy
0001b810 g F .text 00000008_IO_iter_next
0006e34c g F .text 00000cb0_dl_close_worker
00022cd0 g F .text 00000344__valloc
0000929c g F .text 00000198__pthread_init_static_tls
00055624 g F .text 00000014__geteuid
00026f68 g F .text 0000013c_wordcopy_bwd_aligned
…….
从第一列地址看,代码段的地址范围与上面的maps获取到的地址范围是一致的。
2、数据段:
arm-hisiv200-linux-objdump -t thread | grep"\.data"得到如下信息:
00090024 l d .data.rel.ro 00000000.data.rel.ro
000900c8 l d .data 00000000 .data
000900d0 l O .data 00000008stack_used
……………..
0009056c g O .data 00000004_dl_make_stack_executable_hook
00090568 g O .data 00000004_dl_correct_cache_id
00090538 w O .data 00000004 __memalign_hook
00090240 g O .data 000000a0_IO_2_1_stderr_
00090550 g O .data 00000004__progname_full
数据地址与maps一致
3、bss段
arm-hisiv200-linux-objdump -t thread | grep"\.bss"得到如下信息
000907d0 l d .bss 00000000 .bss
000907d0 l O .bss 00000001completed.5624
000907d4 l O .bss 00000004in_flight_stack
000907d8 l O .bss 00000004stack_cache_actsize
000907e4 l O .bss 00000004stack_cache_lock
000907e8 l O .bss 00000008__nptl_threads_events
000907f0 l O .bss 00000004__nptl_last_event
000907f8 l O .bss 00000001__nptl_initial_report_events
00092800 l O .bss 00000208static_slotinfo
00092a08 l O .bss 00000200static_dtv
…..
000941d4 g O .bss 00000004_dl_osversion
000941d8 g O .bss 00000004_dl_inhibit_rpath
000941dc g O .bss 00000004_dl_pagesize
000941e0 g O .bss 0000004c_dl_ns
地址同maps对比发现,最开始的地方使用了数据段的地址空间。
三、strace跟踪进程
通过strace工具跟踪thread创建两个线程的过程:
/home/n5x/strace home/n5x/thread 2:
execve("home/n5x/thread",["home/n5x/thread", "2"], [/* 8 vars */]) = 0
uname({sys="Linux",node="host", ...}) = 0
brk(0) = 0x163e000
brk(0x163ed04) = 0x163ed04
set_tls(0x163e4c0, 0x90014, 0xffffffe0, 0x14,0x92800) = 0
set_tid_address(0x163e068) = 1199
set_robust_list(0x163e070, 0xc) = 0
futex(0x7e85cd14, FUTEX_WAKE_PRIVATE, 1) =0
futex(0x7e85cd14,FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, NULL, 163e08c) = -1 EAGAIN(Resource temporarily unavailable)
rt_sigaction(SIGRTMIN, {0xd870, [],SA_SIGINFO|0x4000000}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {0xd714, [],SA_RESTART|SA_SIGINFO|0x4000000}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1],NULL, 8) = 0
getrlimit(RLIMIT_STACK,{rlim_cur=6144*1024, rlim_max=6144*1024}) = 0
brk(0x165fd04) = 0x165fd04
brk(0x1660000) = 0x1660000
fstat64(1, {st_mode=S_IFCHR|0600,st_rdev=makedev(204, 64), ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICEor TCGETS, {B115200 opost isig icanon echo ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2ab02000
write(1, "stacksize 6291456\n",18stacksize 6291456
) = 18
mmap2(NULL, 6291456, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2ab5a000
mprotect(0x2ab5a000, 4096, PROT_NONE) = 0
clone(child_stack=0x2b158e38,flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,parent_tidptr=0x2b159368, tls=0x2b1597c0, child_tidptr=0x2b159368) = 1200
write(1, "stacksize 6291456\n",18stacksize 6291456
) = 18
mmap2(NULL, 6291456,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b166000
mprotect(0x2b166000, 4096, PROT_NONE) = 0
clone(child_stack=0x2b764e38,flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,parent_tidptr=0x2b765368, tls=0x2b7657c0, child_tidptr=0x2b765368) = 1201
write(1, "i=2\n", 4i=2
) = 4
write(1, "wait a moment, \n",16wait a moment,
) = 16
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) =0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [],0}, 8) = 0
上面的蓝色和紫色分别创建了一个线程,其中有一个mprotect函数创建了4KB与mmap对应的内存保护区,该区域用户无法操作,如果意外操作就会出错。这就是为什么在maps中每个线程会有一个4KB的原因。
四、不同内核进程内存布局
对比海思(3.0.8)和ST(3.4.7)平台,maps中线程mmap增长方向是相反的。说明不同的内核版本采用不同的内存布局,对于早期的内核采用3.0.8的经典内存布局,最新的都是3.4.7的灵活内存布局。
1、经典内存布局
经典布局mmap的增长方向:向上增长,如图:
对于经典模式,heap和mmap都是向上增长的,于是在内核中就为heap保留了一定空间:heap ~mmap起始地址。
mmap起始地址计算方法:
在内核代码/arch/arm/mm/mmap.c
start_addr = TASK_UNMAPPED_BASE
#define TASK_UNMAPPED_BASE (UL(CONFIG_PAGE_OFFSET) / 3)
CONFIG_PAGE_OFFSET= 0xc0000000(对于2G的就是0x80000000)
因此,在这种情况下,mmap可用区域并不是3G,而是2*( CONFIG_PAGE_OFFSET) /3。
2、灵活内存布局
灵活内存布局的增长方向:向下增长,如图:
从图中可以看出,栈至顶向下扩展,并且栈是有界的。对至底向上增长,mmap映射区域至顶向下扩展,直至耗尽虚拟地址空间的剩余区域。
当栈中压入数据超出其容量就会耗尽栈所对应的内存区域,这将触发一个页故障(page fault),并被linux的expand_stack()处理,调用acct_stack_growth来检查是否有合适的地方用于栈的增长。如果栈的大小低于RLIMIT_STACK,那么一般情况下栈会加长。
另外,可以看出栈和mmap映射区域不是从一个固定的地址开始,而是程序启动时有一个随机的偏移,这样使得使用缓冲区溢出攻击的方法更加困难。如果想使地址固定,设置
/proc/sys/kernel/randomize_va_space为0。
也可以将灵活内存布局设置为经典内存布局,设置变量/proc/sys/vm /legacy_va_layout为1。
五、其他补充
嵌入式Linux进程信息及内存布局相关推荐
- 嵌入式linux启动信息完全注释
嵌入式linux启动信息完全注释 from:http://www.embedlinux.cn/ShowPost.asp?ThreadID=377 摘要 我们在这里讨论的是对嵌入式linux系统的启动过 ...
- linux进程中的内存分布
很多小伙伴在调试C代码的时候非常痛苦,C语言不像java那样可以给你指出具体的错误地方和错误原因,C语音因为指针的特殊性和C语言版本的兼容性的需要,很难直接定位到错误的地方.特别是各种段错误.溢出等. ...
- linux用户空间内存分布,了解linux 64位地址空间内存布局
主要搞清楚下列问题: 1.X86-64 页式管理有哪些改变? 2.Linux是怎样支持64bit地址管理? 3.64bit 内存布局是怎么样的? 1. X86-64 页式管理 查看<64-ia- ...
- 【操作系统实验】Linux进程通信—共享内存通信、管道通信
Linux进程通信-共享内存通信.管道通信 一.实验目的: 二.实验题目: 1. 试设计程序利用共享内存完成如下进程通信 1.shmget函数 2.shmat函数 3.shmdt函数 4.shmctl ...
- [内存管理]linux X86_64处理器的内存布局图(转自:http://blog.csdn.net/younger_china/article/details/16829687)
Linux X86 64位内存布局图
- [内存管理]linux X86_64处理器的内存布局图
linux X86 64位内存布局图
- linux 进程装入 物理内存 页表,linux内存管理解析----linux物理,线性内存布局及页表的初始化...
主要议题: 1分页,分段模式及实模式 2Linux分页 3linux内存线性地址空间布局及物理内存空间布局 4linux页表初始化及代码解析 1.1.1内存寻址和保护模式 在X86平台上,内存控制单元 ...
- 查看 linux 硬件信息:内存、分区、系统、环境变量、防火墙、路由、端口监听、进程、CPU...
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 一.linux CPU大小: 其实应该通过Physical Processor ID来区分单核和双核 ...
- Linux 进程资源分配,linux 进程管理和内存分配
1.进程相关概念 进程:正在运行中的程序 内核功用:进程管理.文件系统.网络功能.内存管理.驱动程序.安全功能等 Process:运行中的程序的一个副本,是被载入内存的一个指令集合 进程 ID(Pro ...
- 32位linux进程线程在内存中的样子
1.线程诞生史 1.1 线程诞生的原因 早期是没有线程概念的,只有进程的概念,操作系统以进程为调度单位.--可以这么来理解:早期进程相当于现在的单线程的进程(只有一个线程的进程,创建进程时,里面有一个 ...
最新文章
- java匿名对象赋初值_不想进BAT的Java程序员不是好程序员,BAT后端Java岗面试真题分享
- 大型网站系统架构实践(五)深入探讨web应用高可用方案
- IOS中将对象属性列表归档成一个plist文件中
- 利用Java进行MySql数据库的导入和导出
- Swift 2.3- 3.0
- pycharm 操作的一些设置,记录下
- c语言4x4按键计算器代码,4X4按键实现计算器功能.doc
- python中协程与函数的区别_python协程和异步IO
- VB 使用SendMessage枚举文件与目录
- 今天小小的总结一下最近的小程序中的问题
- matlab 支撑集,基于OMP算法的快速压缩感知图像重构
- docker 设置阿里云加速器
- 信创操作系统--统信UOS桌面版(多媒体软件:图像查看、处理,音频播放、录音机)
- 深度评测 极米z6x和z6哪个好 极米z6和z6x区别
- 网站防御DDoS的方案--高防节点(高防IP)
- c语言 dct变换,DCT, IDCT变换--C语言实现
- Java毕业设计_基于javaEE的论坛的设计和实现
- iOS用户行为追踪——无侵入埋点
- SYN攻击原理以及防范技术
- 机器学习分类算法之随机森林(集成学习算法)