在x86_64下和i386下是类似的,本文主要关注vm.legacy_va_layout以及kernel.randomize_va_space参数影响下的进程空间内存宏观布局,以及vDSO和多线程下的堆和栈分布。


情形一:

  • vm_legacy_va_layout=1
  • kernel.randomize_va_space=0

此种情况下采用传统内存布局方式,不开启随机化,程序的内存布局

可以看出: 代码段:0x400000–> 数据段 堆:向上增长 2aaaaaaab000–> 栈:7ffffffde000<–7ffffffff000 系统调用:ffffffffff600000-ffffffffff601000 你可以试一下其他程序,在kernel.randomize_va_space=0时堆起点是不变的


情形二:

  • vm_legacy_va_layout=0
  • kernel.randomize_va_space=0

现在默认内存布局,不随机化

可以看出: 代码段:0x400000–> 数据段 堆:向下增长 <–7ffff7fff000 栈:7ffffffde000<–7ffffffff000 系统调用:ffffffffff600000-ffffffffff601000


情形三:

  • vm_legacy_va_layout=0
  • kernel.randomize_va_space=2 //ubuntu 14.04默认值 使用现在默认布局,随机化

对比两次启动的cat程序,其内存布局堆的起点是变化的,这从一定程度上防止了缓冲区溢出攻击。


情形四:

  • vm_legacy_va_layout=1
  • kernel.randomize_va_space=2 //ubuntu 14.04默认值 与情形三类似,不再赘述

vDSO

在前面谈了两个不同参数下的进程运行时内存空间宏观的分布。也许你会注意到这样一个细节,在每个进程的stack以上的地址中,有一段动态变化的映射地址段,比如下面这个进程,映射到vdso。 > 

如果我们用ldd看相应的程序,会发现vdso在磁盘上没有对应的so文件。 不记得曾经在哪里看到大概这样一个问题: > getpid,gettimeofday是不是系统调用?

其实这个问题的答案就和vDSO有关,杂x86_64和i386上,getpid是系统调用,而gettimeofday不是。

vDSO全称是virtual dynamic shared object,是一种内核将一些本身应该是系统调用的直接映射到用户空间,这样对于一些使用比较频繁的系统调用,直接在用户空间调用可以节省开销。如果想详细了解,可以参考这篇文档

下面我们用一段程序验证下:

#include <stdio.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <unistd.h>int main(int argc, char **argv)
{struct timeval tv;int ret;if ((ret=gettimeofday(&tv, NULL))<0) {fprintf(stderr, "gettimeofday call failed\n");}else{fprintf(stdout, "seconds:%ld\n", (long int)tv.tv_sec);}fprintf(stdout, "pid:%d\n", (int)getpid());fprintf(stdout, "thread id:%d\n", (int)syscall(SYS_gettid));return 0;
}

编译为可执行文件后,我们可以用strace来验证:

strace -o temp ./vdso
grep getpid temp
grep gettimeofday temp

多线程的堆栈

  • 三个线程的进程:

  • 主线程:

  • 子线程1:

  • 子线程2:

  • 测试代码1:
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>void *routine(void *args)
{fprintf(stdout, "========\n");char arr[10000];fprintf(stdout, "temp var arr address in child thread : %p\n", arr);char arr1[10000];fprintf(stdout, "temp var arr1 address in child thread : %p\n", arr1);fprintf(stdout, "delta : %ld\n", arr1 - arr);for(;;) {sleep(5);}
}int main(int argc, char *argv[])
{// argc 4// argv ?pthread_t pt; // 4pthread_t pt1; // 4int ret;  // 4// pthread max stack size(can be changed): 0x800000 = 8M// char bigArr[0x800000 - 10000]; // SEGMENT FAULT//char arr1[144000];char arr1[144];arr1[0] = 'a';fprintf(stdout, "temp var arr1 address in main thread lower than 139 K : %p\n", arr1);//char arr2[100];char arr2[1];fprintf(stdout, "temp var arr2 address in main thread lower than 139 K : %p\n", arr2);fprintf(stdout, "delta : %ld\n", arr2 - arr1);//char arr3[100];char arr3[10];fprintf(stdout, "temp var arr3 address in main thread lower than 139 K : %p\n", arr3);fprintf(stdout, "delta : %ld\n", arr3 - arr2);ret = pthread_create(&pt, NULL, routine, NULL);ret = pthread_create(&pt1, NULL, routine, NULL);pthread_join(pt, NULL); pthread_join(pt1, NULL); return 0;
}
  • 测试代码2:打印内核栈地址
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <asm/thread_info.h>static int test_param = 10;
module_param(test_param, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(test_param, "a test parameter");static int print_all_processes_init(void)
{struct task_struct *p;for_each_process(p) {if (p->pid == 1) {printk(KERN_INFO "stack : %p\n", p->stack);}};return 0;
}static void print_all_processes_exit(void)
{printk(KERN_INFO "unload module print_all_processes\n");
}module_init(print_all_processes_init);
module_exit(print_all_processes_exit);MODULE_AUTHOR("FEILENGCUI");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A MODULE PRINT ALL PROCESSES");
  • 对应init进程的内核栈stack起始地址

  • 用户态线程栈在同一进程空间的堆起始部分分配,x86_64默认是8M,可以通过ulimit等方法设置
  • 用户态线程栈的增长是从低的线性地址往高增长
  • 内核栈位于高地址
  • 主线程的栈(姑且称为进程栈吧)行为比较怪异,后面会详细分析glibc的ptmalloc下多线程程序malloc和线程栈的内存分配行为

Linux下x86_64进程地址空间布局相关推荐

  1. 查看linux进程的设备io,Linux下查看进程IO工具iopp

    Linux下的IO检测工具最常用的是iostat,不过iostat只能查看到总的IO情况.如果要细看具体那一个程序点用的IO较高,可以使用iotop .不过iotop对内核版本和Python版本有要求 ...

  2. Linux下的进程内存结构

    Linux下的进程内存结构 虚拟内存管理 Linux操作系统采用虚拟内存管理技术,使得每个进程都有独立的地址空间,该地址空间大小为4GB的线性虚拟空间,用户所看到和接触到的都是该虚拟地址,无法看到实际 ...

  3. Linux第二次试验:Linux下的进程通信实验

    Linux第二次试验:Linux下的进程通信实验 前言 一.实验目的 二.实验工具与设备 三.实验预备知识 三.实验内容和步骤 五.实验代码及步骤截图 六.实验总结 前言 为了帮助同学们完成痛苦的实验 ...

  4. Linux下修改MAC地址总结

    偶尔会用到这个知识点,久了不用又会记不住,所以记之,方便以后查询. Linux下修改MAC地址 方法一: 1.关闭网卡设备 ifconfig eth0 down 2.修改MAC地址 ifconfig ...

  5. linux 下得到进程的启动时间

    linux 下得到进程的启动时间! 运行方式:./pstart 进程号 " 如: ./pstart 1 #!/bin/bash pid=$1 if [ "$pid" == ...

  6. linux下查看进程占用端口和端口占用进程命令

    Linux下查看进程占用端口: 查看程序对应进程号:ps –ef|grep 进程名 REDHAT :查看进程号所占用的端口号:netstat –nltp|grep 进程号 ubuntu:查看进程占用端 ...

  7. linux下杀死进程全权讲解

    linux下杀死进程全权讲解 2009-10-27 08:57 佚名 linux 我要评论(0) 字号:T | T 本文将详细讲解linux杀死进程的多种命令,包含他们的作用,kill作用:根据进程号 ...

  8. linux下查看进程的线程数,linux查看进程的线程数

    top -H -p $PID  #查看对应进程的那个线程占用CPU过高 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行 ...

  9. linux 查看进程变量,Linux下查看进程(程序)启动时的环境变量

    Linux下查看进程(程序)启动时的环境变量 Linux的pargs ==================================== 今天又遇到一个老问题: 同事遇到了sqlplus &qu ...

最新文章

  1. c语言是函数式原型的编程,编程范式|程序世界里的编程范式,探索编程本质
  2. Android开发神器:OkHttp框架源码解析
  3. win10 安装microsoft.net framework3.5
  4. cobbler安装和基本配置
  5. MySQL之深入解析一条SQL的执行流程
  6. .htaccess 后门
  7. eBay Notification介绍
  8. html去除分页符,Word如何取消分页符
  9. 华三交换机mode是什么意思_POE交换机150米、长距离250米传输是什么意思?
  10. Shell脚本——变量
  11. 面向对象初调用:foolish 电梯
  12. 企业信息化建设过程中,交通物流行业如何凭借数据成功转型?
  13. 易语言mysql清空一个表_易语言高级表格清空.doc
  14. 程序员面试金典——17.12整数对查找
  15. C#之回到了最初的起点----解决方案、项目、程序集、命名空间
  16. php干货网,php高手干货【必看】
  17. 网络连接正常但百度网页打不开显示无法访问此网站解决方案
  18. 调用sleep后,我做了一个噩梦
  19. 机器学习与算法(12)--最小角回归(LARS)
  20. REDIS11_HyperLogLog的概述、基本命令、UV、PV、DAU、MAU、首页UV如何进行统计处理

热门文章

  1. flash java 6,为Flash构建 Java WebService
  2. 一个月学会Python的Quora指南和资料放送
  3. 咖啡网页设计作品 简单DIV学生网页设计成品 HTML网页学生网页制作模板下载
  4. java.exe闪退_Tomcat7.0 exe闪退问题
  5. uos安装方法_国产UOS(统一操作系统),虚拟机安装体验
  6. 用python计算准确率_分词结果准确率、召回率计算-python(示例代码)
  7. 嘉善 机器人比赛_电脑机器人比赛辅导教师培训在浙师大嘉善附校举行
  8. CAN总线技术 | 数据链路层02 - 帧结构
  9. RTX5 | 配置文件RTX_Config.h(二)
  10. matlab算法大全 pdf_遗传模拟退火算法求解旅行商(TSP)问题