linux 内存管理

      这两天一直为linux内存中的4G的逻辑空间的3:1的用户空间:内核空间的分配纠结,在网上看了不少文档也把那些书都搬出来啦,具体没有看到哪个地方直接解除我心中的疑惑,但从中间也澄清了很多概念。记录如下,以便以后复习。

     在华为笔试的时候有这样一个题,局部变量,全局变量,静态变量分别分别在什么空间中,当时应该是凭感觉写了,结构是很糟糕。在网上看到有关于这点的,转一下,http://blog.csdn.net/kanghua/archive/2007/10/22/1837872.aspx

 

    进程与内存
进程如何使用内存?
毫无疑问所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的。

对任何一个普通进程来讲,它都会涉及到5种不同的数据段。稍有编程知识的朋友都该能想到这几个数据段种包含有“程序代码段”、“程序数据段”、“程序堆栈段”等。不错,这几种数据段都在其中,但除了以上几种数据段之外,进程还另外包含两种数据段。下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区。

代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存种的镜像。代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作——它是不可写的

数据段:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量全局变量

BSS段:BSS段包含了程序中未初始化全局变量,在内存中 bss段全部置零。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味这在数据段中存放变量)。除此以外在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也回被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上将我们可以把堆栈看成一个临时数据寄存、交换的内存区。

进程如何组织这些区域?
上述几种内存区域中数据段、BSS和堆通常是被连续存储的——内存位置上是连续的,而代码段和栈往往会被独立存放。有趣的是堆和栈两个区域关系很“暧昧”,他们一个向下“长”(i386体系结构中栈向下、堆向上),一个向上“长”,相对而生。但你不必担心他们会碰头,因为他们之间间隔很大(到底大到多少,你可以从下面的例子程序计算一下),绝少有机会能碰到一起。

下图简要描述了进程内存区域的分布:(图没有cp过来)

 


BSS
数据段 


代码段


“事实胜于雄辩”,我们用一个小例子(原形取自《User-Level Memory Management》)来展示上面所讲的各种内存区的差别与位置。

#include<stdio.h>

#include<malloc.h>

#include<unistd.h>

int bss_var;

int data_var0=1;

int main(int argc,char **argv)

{

  printf("below are addresses of types of process's mem/n");

  printf("Text location:/n");                                                                     //代码段地方

  printf("/tAddress of main(Code Segment):%p/n",main);

  printf("____________________________/n");

  int stack_var0=2;

  printf("Stack Location:/n");                                                                   //栈段的局部变量的地址

  printf("/tInitial end of stack:%p/n",&stack_var0);

  int stack_var1=3;

  printf("/tnew end of stack:%p/n",&stack_var1);

  printf("____________________________/n");

  printf("Data Location:/n");

  printf("/tAddress of data_var(Data Segment):%p/n",&data_var0);      //数据段变量的地址(初始化的全局变量)

  static int data_var1=4;

  printf("/tNew end of data_var(Data Segment):%p/n",&data_var1);   // 静态数据变量

  printf("____________________________/n");

  printf("BSS Location:/n");

  printf("/tAddress of bss_var:%p/n",&bss_var);                                  // bbs段的未被初始化的全局变量的地址

  printf("____________________________/n");

  char *b = sbrk((ptrdiff_t)0);                                                                // 在堆段分配的局部存储区的地址

  printf("Heap Location:/n");

  printf("/tInitial end of heap:%p/n",b);

  brk(b+4);

  b=sbrk((ptrdiff_t)0);

  printf("/tNew end of heap:%p/n",b);

return 0;

 }

它的结果如下

below are addresses of types of process's mem

Text location:

   Address of main(Code Segment):0x8048388

____________________________

Stack Location:

   Initial end of stack:0xbffffab4

   new end of stack:0xbffffab0     (栈是向下长的)

____________________________

Data Location:

   Address of data_var(Data Segment):0x8049758

   New end of data_var(Data Segment):0x804975c

____________________________

BSS Location:

   Address of bss_var:0x8049864

____________________________

Heap Location:

   Initial end of heap:0x8049868

   New end of heap:0x804986c (堆是向上长的)

 

注意:不管什么样的数据的地址都小于0xC0000000,因为用户区的虚拟空间的地址为0~3G

利用size命令也可以看到程序的各段大小,比如执行size example会得到

text data bss dec hex filename

1654 280   8 1942 796 example

但这些数据是程序编译的静态统计,而上面显示的是进程运行时动态值,但两者是对应的。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/kanghua/archive/2007/10/22/1837872.aspx

 

写到此再来看一个kernel态的程序,看看他的地址是什么样的。

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int __init hello_init(void)
{
 int stack_val0 = 0;
 printk("stack val0's address %p/n",&stack_val0);
 printk("address of the code program %p/n", hello_init);

 printk(KERN_ALERT "hello world!");
 return 0;
}

static void __exit hello_exit(void)
{
 printk(KERN_ALERT "Goodbye!");
}

module_init(hello_init);
module_exit(hello_exit);

 

打印的结果为:

                          stack val0's address d26cdf58
[ 1064.630088] address of the code program e08ef000
[ 1064.630124] hello world!

可以看到kernel中的地址都是大于0xC0000000.

这也许能证明经常看到的0~3G为用户空间,3G~4G为内核空间的说法啦!(这里的空间是逻辑地址,而并非物理地址。上面我的程序打印的地址都是逻辑地址)。

分析如下:

首先在分析linux内存管理的时候要搞清楚3个地址的关系,逻辑地址,线性地址,物理地址。不想说的太复杂,简单地说,

逻辑地址是相对于用人说的,不论是用户空间的人还是kernel空间的人。

线性地址是对cpu来说的,cpu看到的是线性地址。

物理地址是物理内存来说的,是真正的物理地址。

但具体这三个地址之间如何转换的,有内存分页机制和内存分段机制的区别。网上很多分析这点的文档的。

现在系统中有一个1G的内存,那么这个1G的内存和上面的4G的空间是什么的关系,用户空间和kernel空间在这1G的内存上又是如何分配的呢,这也是一直困扰我的问题。

在具体的1G内存上,kernel放在什么地方,用户空间有分配在什么地方呢,可能需要结合一个具体的系统来分析(arm/x86/mips),没有具体研究是由体系决定还是有bootloader决定的。这和逻辑空间的分配没有任何关系(3:1).

3:1的关系是用在逻辑地址空间,例如在用户空间的程序分配的内存的地址是小于0xC0000000(0~3G之间),假如这个地址为xxxx,具体这个地址对应于哪个物理地址,是要经过地址转换后才知道的,但这并不需要我们用户空间的用户知道。例如在kernel空间运行的程序分配的内存空间的地址肯定大于0xC0000000(3G~4G之间).

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值