Linux 内存管理基础

1、地址

物理地址:出现在CPU地址总线上的寻址物理内存的地址信号,是地址变换的最终结果

线性地址(虚拟地址):

逻辑地址: 程序代码经过编译后,出现在汇编程序中的地址

 

地址转换:

逻辑地址转换物理地址:CPU利用段式内存管理单元,将逻辑地址转换成线性地址,在利用页式内存管理单元,把线性地址转换成物理地址

段式内存(X86):

16位CPU:内部有20位的地址线,寻址范围是1M的内存空间,但是用于存放地址的寄存器(IP,SP)只有16位,因此只能访问65536个单元存储,64K

为了管理1M的内存空间,在CPU内部增加了段寄存器。16位CPU把1M内存空间分为若干个逻辑段,每个逻辑段的要求如下:

1)逻辑段的其实地址(段地址)必须是16的倍数,即最后的4个二进制位必须为0

2)逻辑段的最大容量是64K

段寄存器是为了对内存进行分段管理而增加的16位CPU有4个段寄存器,程序同时可以访问4个含义不同的段:

1)CS+IP:用于代码段的访问,CS指向存放程序的段基址,IP指向下条要执行的指令在CS段的偏移量,通过这两个寄存器就可以得到一个内存物理地址(存放下条指令)

2)SS+SP:用于堆栈段的访问,SS指向堆栈段的及地址,SP指向栈顶,可以通过SS+SP两个寄存器直接访问栈顶单元的内存物理位置

3)DS+BX:用于数据段的访问,DS中的值左移四位得到数据段起始地址,再加上BS中的偏移量,得到一个存储单元的物理地址

4)ES+BX:用于附加段的访问,ED中的值左移四位得到附加段起始地址,再加上BX中的偏移量,得到一个存储单元的物理地址

两个寄存器:段基地址寄存器和段偏移寄存器

逻辑地址=段基地址+段内偏移量

由逻辑地址得到物理地址的公式:

PA=段寄存器的值*16+逻辑地址

32位CPU:同样采用分段的管理模式,与16位CPU的不同之处在于32位采用两种不同的工作方式:实模式和保护模式

实模式 :内存管理和16位CPU一致

保护模式:段基地址长32位,每个段的最大容量是4G,段寄存器的值是段地址的“选择器”,用该选择器从内存中得到一个32位的段地址,存储单元的物理地址就是该短地址加上段内偏移值

 

页式内存:线性地址被分为固定长度的组,称为页(虚拟地址)

物理页:分页单元把所有的物理内存划分成固定长度的管理单元,称为物理页,或者是页框、页帧(物理地址)

Linux内存中所有段的及地址均为0,因此逻辑地址与线性地址保持一致,在Linux中(逻辑地址,线性地址和虚拟地址)可以认为是一致的

 

2、虚拟内存(Linux进程地址空间)

Linux操作系统采用虚拟内存管理技术,使得每个进程都有独立的进程地址空间。利用虚拟地址不但能起到保护操作系统的作用,而且更重要的是用户程序可使用比实际物理内存更大的地址空间。

用户空间:每个进程独立拥有3G的用户空间,0~0xbfffffff,每当进程切换时,用户空间就会跟着变化

可以通过 /proc/pid/maps 命令查看进程使用的线性地址

实际的物理内存只有当进程真的访问新获取的虚拟地址时,才会由“请页机制”产生“缺页”异常,从而进入分配实际页框的程序。该异常是虚拟内存机制赖以存在的基本保证--它会告诉内核去位进程分配物理页,并建立对应的页表,之后虚拟内存才试试再在的映射到物理地址上。

申请内存:

按页分配:

get_zaroed_page(unsigned int flags)//返回指向新页面的指针,并将页面清零

__get_free_page(unsigned int flags)//返回指向新页面的指针,但不页面清零 --void free_page(unsigned long addr)

__get_free_pages(unsigned int flags,unsigned int order)//,分配若干个连续的页面,返回指向该内存区域的指针,不页面清零--void free_pages(unsigned long addr,unsigned long order)

3、Linux内核地址空间

高端内存:物理内存896MB以上的部分称为高端内存

内核空间分布:

直接映射区 8M 动态映射区 8K KMAP区 固定映射区 4K

| 896M(MB) | 120M(min) | 4M | 4M |

直接内存映射区:从3G开始,最大896M的线性地址区间,该区域的线性地址和物理地址之间存在线性转换关系:

线性地址 = 3G + 物理地址

动态映射区:由vmalloc来进行分配,特点是线性空间连续,但是对应的物理空间不一定连续(物理页可能处于地端内存,也可能处于高端内存)

永久内存映射区(PKMAP):可以访问896M以上的所有内存,访问方法:

1)使用alloc_page(__GFP_HIGHMEM)分配高端内存页

2)使用kmap函数将分配到的高端内存映射到该区域

固定映射区:和4G顶端只有4K的隔离带,固定映射区中每个地址项都服务于特定的用途

4、内核链表

链表的开销主要是访问的顺序性和组织链的空间损失,

内核链表具备双链表功能,,通常都组织成双向循环链表

内核链表操作:

初始化链表头

INIT_LIST_HEAD(list_head *head);

插入节点:

list_add(struct list_head *new,struct list_head *head) //链表头之后插入

list_add_tail(struct list_head *new,struct list_head *head)//链表末尾加入

list_del(struct list_head *entry); //删除节点

提取数据结构

list_entry(ptr,tye,member)//,已知数据结构中的节点指针ptr,找出数据结构

遍历:

list_for_each(struct list_head *pos,struct list_head *head);

5、Linux内核定时器

时间中断由系统的定时硬件以周期性的时间间隔产生,这个时间间隔(频率)由内核根据HZ来确定

每当时钟中断发生时,全局变量jiffies(unsigned long)加1,jiffies记录了自linux启动后时钟中断发生的次数,驱动程序常利用jiffies来计算不同事件间的时间间隔

 

Linux内核定时器被组织成双向链表,并使用struct timer_list结构描述:

struct timer_list {

struct list_head entry;//内核使用

unsigned long expires;//超时的jiffies值,timer.expires= jiffies + (x * HZ);

void (*function)(unsigned long);//超时处理函数,function(int para) { // para 等于 超时时间的值}

unsigned long data;//超时处理函数参数

struct tvec_base *base;//内核使用

操作定时器:

初始化定时器队列结构

void init_timer(struct timer_list *timer);

启动定时器

void add_timer(struct timer_list *timer);

删除定时器

int del_timer(struct timer_list *timer); //在定时器超时前将它删除,当定时器超时后,系统会自动将它删除

};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值