`
mqzhuang
  • 浏览: 185092 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

linux进程内存布局

阅读更多

 

内存管理是操作系统的核心之一,最近在研究内核的内存管理以及 C 运行时库对内存的分配和管理,涉及到进程在内存的布局,在此对进程的内存布局做一下总结:

 

1. 32 位模式下的 linux 内存布局

图上的各个部分描述得比较清楚,不需再做过多的描述。从上图可以看到,栈至顶向下扩展,并且栈是有界的。堆至底向上扩展, mmap 映射区域至顶向下扩展, mmap 映射区域和堆相对扩展,直至耗尽虚拟地址空间中的剩余区域,这种结构便于 C 运行时库使用 mmap 映射区域和堆进行内存分配。上图的布局形式是在内核 2.6.7 以后才引入的,这是 32 位模式下的默认内存布局形式。看看 cat 命令在 2.6.36 上内存布局:

08048000-08051000 r-xp 00000000 08:01 786454     /bin/cat

08051000-08052000 r--p 00008000 08:01 786454     /bin/cat

08052000-08053000 rw-p 00009000 08:01 786454     /bin/cat

08053000-08074000 rw-p 00000000 00:00 0          [heap]

b73e3000-b75e3000 r--p 00000000 08:01 400578     /usr/lib/locale/locale-archive

b75e3000-b75e4000 rw-p 00000000 00:00 0

b75e4000-b773b000 r-xp 00000000 08:01 1053967    /lib/libc-2.12.1.so

b773b000-b773c000 ---p 00157000 08:01 1053967    /lib/libc-2.12.1.so

b773c000-b773e000 r--p 00157000 08:01 1053967    /lib/libc-2.12.1.so

b773e000-b773f000 rw-p 00159000 08:01 1053967    /lib/libc-2.12.1.so

b773f000-b7742000 rw-p 00000000 00:00 0

b774f000-b7750000 r--p 002a1000 08:01 400578     /usr/lib/locale/locale-archive

b7750000-b7752000 rw-p 00000000 00:00 0

b7752000-b7753000 r-xp 00000000 00:00 0          [vdso]

b7753000-b776f000 r-xp 00000000 08:01 1049013    /lib/ld-2.12.1.so

b776f000-b7770000 r--p 0001b000 08:01 1049013    /lib/ld-2.12.1.so

b7770000-b7771000 rw-p 0001c000 08:01 1049013    /lib/ld-2.12.1.so

bfbed000-bfc0e000 rw-p 00000000 00:00 0          [stack]

 

可以看到,栈和 mmap 映射区域并不是从一个固定地址开始,并且每次的值都不一样,这是程序在启动时随机改变这些值的设置,使得使用缓冲区溢出进行攻击更加困难。当然也可以让程序的栈和 mmap 映射区域从一个固定位置开始,只需要设置全局变量 randomize_v a_space 值为 0 ,这个变量默认值为 1 。用户可以通过设置 /proc/sys/kernel/randomize_va_space 来停用该特性,也可以用如下命令:

sudo sysctl -w kernel.randomize_va_space=0

 

设置 randomize_va_space 0 后,再看看 cat 的内存布局:

08048000-08051000 r-xp 00000000 08:01 786454     /bin/cat

08051000-08052000 r--p 00008000 08:01 786454     /bin/cat

08052000-08053000 rw-p 00009000 08:01 786454     /bin/cat

08053000-08074000 rw-p 00000000 00:00 0          [heap]

b7c72000-b7e72000 r--p 00000000 08:01 400578     /usr/lib/locale/locale-archive

b7e72000-b7e73000 rw-p 00000000 00:00 0

b7e73000-b7fca000 r-xp 00000000 08:01 1053967    /lib/libc-2.12.1.so

b7fca000-b7fcb000 ---p 00157000 08:01 1053967    /lib/libc-2.12.1.so

b7fcb000-b7fcd000 r--p 00157000 08:01 1053967    /lib/libc-2.12.1.so

b7fcd000-b7fce000 rw-p 00159000 08:01 1053967    /lib/libc-2.12.1.so

b7fce000-b7fd1000 rw-p 00000000 00:00 0

b7fde000-b7fdf000 r--p 002a1000 08:01 400578     /usr/lib/locale/locale-archive

b7fdf000-b7fe1000 rw-p 00000000 00:00 0

b7fe1000-b7fe2000 r-xp 00000000 00:00 0          [vdso]

b7fe2000-b7ffe000 r-xp 00000000 08:01 1049013    /lib/ld-2.12.1.so

b7ffe000-b7fff000 r--p 0001b000 08:01 1049013    /lib/ld-2.12.1.so

b7fff000-b8000000 rw-p 0001c000 08:01 1049013    /lib/ld-2.12.1.so

bffdf000-c0000000 rw-p 00000000 00:00 0          [stack]

可以看出,栈和 mmap 区域都从固定位置开始了, stack 的起始位置为 0x c0000000 mmap 区域的起始位置为 0x b8000000 ,可见系统为 stack 区域保留了 128M 内存地址空间。

 

在某些情况下,设置 randomize_va_space 0 ,便于对系统做一些针对性的研究,例如:进程的内存映射有个叫 vdso 的区域,也就是用 ldd 命令看到的那个” linux-gate.so.1 “,这块区域可以看成是内核用于实现 vsyscall 而创建的 virtual shared object ,遵循 elf 的格式,并且可以被用户程序访问。在设置 randomize_va_space 0 的情况下,使用如下命令就可以把这个区域 dump 出来看过究竟。如果不设置 randomize_va_space ,每次 vdso 的地址都是随机的,下面的命令也无能为力。

zhuang@ubuntu:~$ dd if=/proc/self/mem of=gate.so bs=4096 skip=$[0xb7fe1] count=1

dd: `/proc/self/mem': cannot skip to specified offset

1+0 records in

1+0 records out

4096 bytes (4.1 kB) copied, 0.00144225 s, 2.8 MB/s

zhuang@ubuntu:~$ objdump -d gate.so

 

gate.so:     file format elf32-i386

 

 

Disassembly of section .text:

 

ffffe400 <__kernel_sigreturn>:

ffffe400:        58                           pop    %eax

ffffe401:        b8 77 00 00 00               mov    $0x77,%eax

ffffe406:        cd 80                        int    $0x80

ffffe408:        90                           nop

ffffe409:        8d 76 00                     lea    0x0(%esi),%esi

 

ffffe40c <__kernel_rt_sigreturn>:

ffffe40c:        b8 ad 00 00 00               mov    $0xad,%eax

ffffe411:        cd 80                        int    $0x80

ffffe413:        90                           nop

 

ffffe414 <__kernel_vsyscall>:

ffffe414:        51                           push   %ecx

ffffe415:        52                           push   %edx

ffffe416:        55                           push   %ebp

ffffe417:        89 e5                        mov    %esp,%ebp

ffffe419:        0f 34                        sysenter

ffffe41b:        90                           nop

ffffe41c:        90                           nop

ffffe41d:        90                           nop

ffffe41e:        90                           nop

ffffe41f:        90                           nop

ffffe420:        90                           nop

ffffe421:        90                           nop

ffffe422:        eb f3                        jmp    ffffe417 <__kernel_vsyscall+0x3>

ffffe424:        5d                           pop    %ebp

ffffe425:        5a                           pop    %edx

ffffe426:        59                           pop    %ecx

ffffe427:        c3                           ret

 

2. 32 为模式下的经典布局:

这种布局 mmap 区域与栈区域相对增长,这意味着堆只有 1GB 的虚拟地址空间可以使用,继续增长就会进入 mmap 映射区域,这显然不是我们想要的。这是由于 32 模式地址空间限制造成的,所以 内核引入了前一种虚拟地址空间的布局形式。但是对 64 位模式,提供了巨大的虚拟地址空间,这个布局就相当好。如果要在 2.6.7 以后的内核上使用 32 位模式内存经典布局,有两种办法可以设置:

方法一: sudo sysctl -w vm.legacy_va_layout=1

方法二: ulimit -s unlimited

 

同时设置 randomize_va_space 0 后, cat 的内存布局已经回到经典形式了:

08048000-08051000 r-xp 00000000 08:01 786454     /bin/cat

08051000-08052000 r--p 00008000 08:01 786454     /bin/cat

08052000-08053000 rw-p 00009000 08:01 786454     /bin/cat

08053000-08074000 rw-p 00000000 00:00 0          [heap]

40000000-4001c000 r-xp 00000000 08:01 1049013    /lib/ld-2.12.1.so

4001c000-4001d000 r--p 0001b000 08:01 1049013    /lib/ld-2.12.1.so

4001d000-4001e000 rw-p 0001c000 08:01 1049013    /lib/ld-2.12.1.so

4001e000-4001f000 r-xp 00000000 00:00 0          [vdso]

4001f000-40021000 rw-p 00000000 00:00 0

40021000-40022000 r--p 002a1000 08:01 400578     /usr/lib/locale/locale-archive

4002f000-40186000 r-xp 00000000 08:01 1053967    /lib/libc-2.12.1.so

40186000-40187000 ---p 00157000 08:01 1053967    /lib/libc-2.12.1.so

40187000-40189000 r--p 00157000 08:01 1053967    /lib/libc-2.12.1.so

40189000-4018a000 rw-p 00159000 08:01 1053967    /lib/libc-2.12.1.so

4018a000-4018e000 rw-p 00000000 00:00 0

4018e000-4038e000 r--p 00000000 08:01 400578     /usr/lib/locale/locale-archive

bffdf000-c0000000 rw-p 00000000 00:00 0          [stack]

 

3. 64 位模式下的内存布局

64 位模式下各个区域的起始位置是什么呢?对于 AMD64 内存布局采用的是经典模式, text 的起始地址为 0x0000000000400000 ,堆紧接着 BSS 段向上增长, mmap 映射区域开始位置一般设为 TASK_SIZE/3

#define TASK_SIZE_MAX   ((1UL << 47) - PAGE_SIZE)

#define TASK_SIZE               (test_thread_flag(TIF_IA32) ? \
                                        IA32_PAGE_OFFSET : TASK_SIZE_MAX)
#define STACK_TOP               TASK_SIZE

#define TASK_UNMAPPED_BASE      (PAGE_ALIGN(TASK_SIZE / 3))

计算一下可知, mmap 的开始区域地址为 0x0000 2AAAAAAAA000,栈顶地址为 0x0000 7FFFFFFFF000

分享到:
评论
1 楼 xly931 2013-03-25  
请教大神,其实我有点疑惑的是为什么同一个库有映射到进程空间中,竟然有两个相同的偏移量。例如:
40186000-40187000 ---p 00157000 08:01 1053967    /lib/libc-2.12.1.so
40187000-40189000 r--p 00157000 08:01 1053967    /lib/libc-2.12.1.so
上面的偏移量都是00157000 ,但是映射到了不同的进程空间。到底映射的是什么数据,我只是入门,查阅资料,没有结果

相关推荐

    glibc内存管理ptmalloc源代码分析

    2.1 X86平台Linux进程内存布局 2.1.1 32位模式下进程内存经典布局 2.1.2 32位模式下进程默认内存布局 2.1.3 64位模式下进程内存布局 2.2 操作系统内存分配的相关函数 2.2.1 Heap操作相关函数 2.2.2 Mmap映射...

    linux进程的一切知识.zip

    linux进程的一切知识.zip 进程占用的内存空间布局,虚拟空间地址分布 进程启动的3种方式 监控子进程的状态 进程的终止 僵尸进程

    疯狂内核之——Linux虚拟内存

    1.4 Linux内存布局 21 1.5 内核空间和用户空间 23 1.5.1 初始化临时内核页表 24 1.5.2 永久内核页表的初始化 32 1.5.3 第一次进入用户空间 41 1.5.4 内核映射机制实例 44 1.6 固定映射的线性地址 48 1.7 高端内存...

    深入分析Linux内核源码.chm

    4.2 Linux中的进程概述 4.3 task_struct结构描述 4.4 task_struct结构在内存中的存放 4.5 进程组织的方式 4.6 内核线程 4.7 进程的权能 4.8 内核同步 第五章进程调度 5.1 Linux时间系统 5.2 时钟中断 5.3 Linux的...

    Linux虚拟地址空间布局

    在Linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3,而Windows系统为2:2(通过设置Large-Address-Aware Executables标志也可为1:3)。这并不意味着内核使用那么多物理内存,仅表示它可支配这部分地址空间,...

    清华大学Linux操作系统原理与应用

    3.4.3 Linux进程调度时机 50 3.4.4 进程调度的依据 51 3.4.5 调度函数schedule()的实现 52 3.5 进程的创建 54 3.5.1 创建进程 55 3.5.2 线程及其创建 56 3.6 与进程相关的系统调用及其应用 58 3.6.1 fork系统调用 58...

    深入分析Linux内核源码

    5.3.2 Linux进程调度时机 5.3.3 进程调度的依据 5.3.4 进程可运行程度的衡量 5.3.5 进程调度的实现 5.4 进程切换 5.4.1 硬件支持 5.4.2 进程切换 第六章 Linux内存管理 6.1 Linux的内存管理概述 6.1.1 ...

    LINUX.UNIX系统编程手册(下册)

    《linux/unix系统编程手册(上、下册)》总共分为64章,主要讲解了高效读写文件,对信号、时钟和定时器的运用,创建进程、执行程序,编写安全的应用程序,运用posix线程技术编写多线程程序,创建和使用共享库,运用...

    LINUX网站建设技术指南

    5.1.6 各服务进程的作用 5.2 建立PPP连接和配置PPP服务器 5.2.1 有关PPP的基础知识 5.2.2 使用PPP拨号上网 5.2.3 配置PPP服务器 5.3 配置DNS服务器 5.3.1 DNS(Domain Name System)简介 5.3.2 域名服务系统 5.3.3 ...

    课堂笔记_Linux_系统编程1

    不管怎样,下面是一个Linux进程的标准的内存段布局:当计算机开心、安全、可爱、正常的运转时,那么上图显示的内存段起始虚拟地址在几乎每个进程中都是一样的。也许是

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

    18.5 内存布局374 18.6 内核移植375 18.7 嵌入式驱动程序376 18.7.1 闪存377 18.7.2 uart377 18.7.3 按钮和滚轮378 18.7.4 pcmcia/cf378 18.7.5 sd/mmc378 18.7.6 usb378 18.7.7 rtc378 18.7.8...

    Linux/UNIX系统编程手册.part2

    《Linux/UNIX系统编程手册(上、下册)》总共分为64章,主要讲解了高效读写文件,对信号、时钟和定时器的运用,创建进程、执行程序,编写安全的应用程序,运用POSIX线程技术编写多线程程序,创建和使用共享库,运用...

    Linux/UNIX系统编程手册.part1

    《Linux/UNIX系统编程手册(上、下册)》总共分为64章,主要讲解了高效读写文件,对信号、时钟和定时器的运用,创建进程、执行程序,编写安全的应用程序,运用POSIX线程技术编写多线程程序,创建和使用共享库,运用...

    精通LINUX设备驱动程序开发

    367 第18章 嵌入式linux 369 18.1 挑战 369 18.2 元器件选择 370 18.3 工具链 371 18.4 bootloader 372 18.5 内存布局 374 18.6 内核移植 375 18.7 嵌入式驱动程序 376 18.7.1 闪存 377 18.7.2 uart 377 ...

    awesome-linux-system-cn:收集网络上讲解Linux系统原理的优秀文章、文档

    采集网络上关于Linux系统相关的优秀资源。 目录 ...Anatomy of a Program in Memory:程序的内存布局 How The Kernel Manages Your Memory:内核如何管理内存 Understanding the Memory Layout of L

    存储管理分析

    内存使用的布局和初始化 基于分页的内存管理 进程与内存管理

    《Linux系统编程、网络编程》第5章 进程控制

    进程与程序、进程PID与三个特殊进程、fork函数的作用、父子进程对文件的共享、execve加载函数、system函数、进程的资源回收、wait函数、进程状态、java进程、进程关系、守护进程,return/exit/_exit、环境变量表、...

Global site tag (gtag.js) - Google Analytics