linux内存管理-内核用户空间 【转】

转自:http://blog.chinaunix.net/uid-25909619-id-4491362.html

1,linux内存管理中几个重要的结构体和数组

page


unsigned long flags


一组标志,也对页框所在的管理区进行编号


atomic_t _count


该页被引用的次数


atomic_t _mapcount


页框中页表项数目,如果没有则为-1


struct list_head lru


管理page忙碌/空闲链表(inactive_list/active_list),protected by zone->lru_lock !

zone


struct free_area free_area[MAX_ORDER]


标识出管理区中的空闲页框块(buddy system)


struct pglist_data *zone_pgdat


该zone的一些属性,包括指向各个node_zone指针等


unsigned long zone_start_pfn


zone start page frame number,zone在mem_map数组中的起始页号。/* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */

pglist_data


struct zone node_zones[MAX_NR_ZONES]


节点中管理区描述符数组


struct zonelist  node_zonelists[MAX_ZONELISTS]


页分配器使用的zonelist数据结构的数组


int nr_zones


节点中管理区的个数


struct page *node_mem_map


节点中页描述符的数组


unsigned long node_start_pfn


节点中第一个页框的下标

mem_map

初始化调用路径:

free_area_init_node()

alloc_node_mem_map()

mem_map = NODE_DATA(0)->node_mem_map;

.

2,各个结构体之间的关系

3,主要的内存分配函数

vmalloc

1,vmalloc 申请返回的地址在vmalloc_start到vmalloc_end之间。其中,vmalloc_start在虚拟地址:

3G+physic memory length + 8G(gap)

vmalloc_end位置在虚拟地址:

4G -128K(专用页面映射)

2,kmalloc对应于kfree,可以分配连续的物理内存;

3,vmalloc优先使用高端物理内存,但性能上会打些折扣。

vmalloc分配的物理页不会被交换出去;

vmalloc使用的是vmlist链表,与管理用户进程的vm_area_struct要区别,而后者会swapped。

kmalloc

1,kmalloc申请返回内核虚拟地址,这个虚拟地址与实际的物理地址之间存在偏移量0XC000_0000,可用用virt_to_phys() 得到对应的物理地址。

3G~physic

2,vmalloc对应于vfree,分配连续的虚拟内存,但是物理上不一定连续。

3,kmalloc分配内存是基于slab,因此slab的一些特性包括着色,对齐等都具备,性能较好。物理地址和逻辑地址都是连续的。

4, kmalloc()是内核中最常见的内存分配方式,它最终调用伙伴系统的__get_free_pages()函数分配,根据传递给这个函数的flags参数,决定这个函数的分配适合什么场合,如果标志是GFP_KERNEL则仅仅可以用于进程上下文中,如果标志GFP_ATOMIC则可以用于中断上下文或者持有锁的代码段中。

kmalloc返回的线形地址是直接映射的,而且用连续物理页满足分配请求,且内置了最大请求数(2**5=32页)。

5,kmalloc 能够处理的最小分配是 32 或者 64 字节(依赖系统的体系所使用的页大小),小于128K

kmap

kmap()是主要用在高端存储器页框的内核映射中,一般是这么使用的:

使用alloc_pages()在高端存储器区得到struct page结构,然后调用kmap(struct *page)在内核地址空间PAGE_OFFSET+896M之后的地址空间中(PKMAP_BASE到FIXADDR_STAR)建立永久映射(如果page结构对应的是低端物理内存的页,该函数仅仅返回该页对应的虚拟地址)

kmap()也可能引起睡眠,所以不能用在中断和持有锁的代码中

不过kmap 只能对一个物理页进行分配,所以尽量少用。

使用kmap的原因:

对于高端物理内存(896M之后),并没有和内核地址空间建立一一对应的关系(即虚拟地址=物理地址+PAGE_OFFSET这样的关系),所以不能使用get_free_pages()这样的页分配器进行内存的分配,而必须使用alloc_pages()这样的伙伴系统算法的接口得到struct *page结构,然后将其映射到内核地址空间,注意这个时候映射后的地址并非和物理地址相差PAGE_OFFSET。

get_user_pages

用于从用户空间获取缓冲区地址(页对齐),直接进行IO操作。通常用于大数据量的操作,如DMA。

其访问流程如下:

4,其他一些杂

gfp_mask

有三个作用:

1,行为修饰  使用指定的方法分配内存。例如GPF_WAIT,可睡眠;GPF_IO,可启动磁盘。

2,区修饰   标识从哪个分区分配内存

3,类型修饰  例如GFP_KERNEL=>__FGP_WAIT | __GFP_IO | __GFP_FS,指定所需行为和区描述符。

migrate_type

#define MIGRATE_UNMOVABLE     0

#define MIGRATE_RECLAIMABLE   1

#define MIGRATE_MOVABLE       2

#define MIGRATE_PCPTYPES      3 /* the number of types on the pcp lists */

#define MIGRATE_RESERVE       3

#define MIGRATE_ISOLATE       4 /* can‘t allocate from here */

#define MIGRATE_TYPES         5

对伙伴系统的改进,减少系统碎片。

struct free_area {

struct list_head    free_list[MIGRATE_TYPES];

unsigned long        nr_free;

};

每一个free_area包含多个链表,其中每一个链表中的内存页面按照其自身是否可以释放或者迁移被归为一类,于是凡是请求“不可迁移”页面的分配请求全部在free_list[MIGRATE_UNMOVABLE]这条链表上分配,和老版本一样,系统中有10个free_area代表大小为2的N次幂个不同页面的集合。这种归类可以最小化内存碎片。

linux内存管理(2)-用户空间

1. 编译链接的一些知识

首先,我们来编写一个简单的程序,示例代码如下

[cpp] view plaincopyprint?

  1. #include "stdio.h"
  2. #include "string.h"
  3. #include "stdlib.h"
  4. int i=3;
  5. int j=4;
  6. int main()
  7. {
  8. printf("value i is %d\n,address of i is 0x%lx\naddress of j is 0x%lx\n",i,(unsigned int)&i,(unsigned int )&j);
  9. }

程序片段1

程序片段1定义了两个变量,a和b,并分别赋值为3,4。然后在主函数main中,打印了i和j的值及其对其对应的虚拟地址。程序的运行结果如下:

[cpp] view plaincopyprint?

  1. value i is 3
  2. address of i is 0x80495e0,
  3. address of j is 0x80495e4

片段1

程序片段2中所示的0x80495e0,0x80495e4分别是i和j的虚拟地址,那么,这个地址是怎么确定的?由谁来确定?

通过查看编译后的可执行程序的符号列表,i和j的地址实际上是在链接的过程中确定的,由编译器来确定。

[cpp] view plaincopyprint?

  1. objdump -t a.out
  2. a.out:     file format elf32-i386
  3. SYMBOL TABLE:
  4. ......
  5. 00000000  w      *UND*  00000000              _Jv_RegisterClasses
  6. 08048494 g     O .rodata        00000004              _fp_hw
  7. 08048478 g     F .fini  00000000              _fini
  8. 00000000       F *UND*  0000019f              [email protected]@GLIBC_2.0
  9. 08048498 g     O .rodata        00000004              _IO_stdin_used
  10. 080495dc g       .data  00000000              __data_start
  11. 080495e0 g     O .data  00000004              i
  12. 0804849c g     O .rodata        00000000              .hidden __dso_handle
  13. 080494f0 g     O .dtors 00000000              .hidden __DTOR_END__
  14. 080483e0 g     F .text  00000069              __libc_csu_init
  15. 00000000       F *UND*  00000039              [email protected]@GLIBC_2.0
  16. 080495e8 g       *ABS*  00000000              __bss_start
  17. 080495e4 g     O .data  00000004              j
  18. 080495f0 g       *ABS*  00000000              _end
  19. 080495e8 g       *ABS*  00000000              _edata
  20. 08048449 g     F .text  00000000              .hidden __i686.get_pc_thunk.bx
  21. 08048384 g     F .text  00000044              main
  22. 08048250 g     F .init  00000000              _init

2. 管理用户空间进程内存的结构体

当一个程序被载入内存,内核就会为其建立一个名为task_struct的结构体来管理这个进程。

[cpp] view plaincopyprint?

  1. struct task_struct {
  2. struct list_head tasks;
  3. struct mm_struct *mm, *active_mm;//字段mm为该进程的内存管理。
  4. pid_t pid;
  5. struct fs_struct *fs;
  6. struct files_struct *files;
  7. ......
  8. };

task_struct

[cpp] view plaincopyprint?

  1. struct mm_struct {
  2. struct vm_area_struct * mmap; /* list of VMAs ,一个进程有一个mm_struct,多个
  3. vm_area_struct*/
  4. pgd_t * pgd;
  5. int map_count; /* number of VMAs */
  6. ......
  7. };

mm_struct

[cpp] view plaincopyprint?

  1. struct vm_area_struct {
  2. struct mm_struct * vm_mm; /* The address space we belong to. */
  3. unsigned long vm_start; /* Our start address within vm_mm. */
  4. unsigned long vm_end; /* The first byte after our end address
  5. within vm_mm. */
  6. }

vm_area_struct

小结:整个内存管理的体系可以这么理解,在32位系统上,所有的地址(0-4G),页表、页框等都统一由内核管理。而内核中的服务(这里的服务包括内运行在内核中的进程,内核变量等)只是占用了3-4G这个地址空间段。对于用户空间而言,进程的地址是由编译器决定的,编译器在链接各个库文件时,用了0-3G的地址。

对于硬件MMU模块而言,它并不关心Linux给它的地址属于哪个空间,MMU只是根据所设定的规则(内存映射表)找到对应的物理地址。

各结构体之间关系图

3. 用户空间malloc的实现

由前面的小结知道,内存管理都在内核空间进行。对于malloc申请的一块内存也是一样,先来看一下malloc调用的图示:

malloc->brk()->SYSCALL_DEFINE1(brk, unsigned long, brk)->__get_free_pages()

SYSCALL_DEFINE1这个系统调用,将执行的状态从用户空间转向内核空间。

时间: 2024-10-12 03:55:05

linux内存管理-内核用户空间 【转】的相关文章

(转)linux 内存管理——内核的shmall 和shmmax 参数

内核的 shmall 和 shmmax 参数 SHMMAX= 配置了最大的内存segment的大小 ------>这个设置的比SGA_MAX_SIZE大比较好. SHMMIN= 最小的内存segment的大小 SHMMNI= 整个系统的内存segment的总个数 SHMSEG= 每个进程可以使用的内存segment的最大个数 配置信号灯( semphore )的参数: SEMMSL= 每个semphore set里面的semphore数量 -----> 这个设置大于你的process的个数吧,

Linux内存管理-内核的shmall和shmmax参数(性能调优)(转)

内核的shmall和shmmax参数 SHMMAX=配置了最大的内存segment的大小:这个设置的比SGA_MAX_SIZE大比较好. SHMMIN=最小的内存segment的大小 SHMMNI=整个系统的内存segment的总个数 SHMSEG=每个进程可以使用的内存segment的最大个数 配置信号灯( semphore )的参数: SEMMSL=每个semphore set里面的semphore数量:这个设置大于你的process的个数吧,否则你不得不分多个semphore set,好像

【研究任务】linux内存管理机制——内核空间

Linux内存中线性地址为4G,0~3G为用户空间,3~4G为内核空间 一.      内核空间 内核空间是3~4G的内存地址,主要用来存储高优先级的代码 在X86结构中的内核地址存在三种类型的区域: ZONE_DMA     内存开始的16m ZONE_NORMAL       16m~896m ZONE_HIGHMEM    896M~ ZONE_DMA是DMA使用的页(DMA是直接路径访问,不经过cpu缓存而直接访问内存)ZONE_NORMAL是正常可寻址的页.ZONE_HIGHMEM是动

Linux内存管理--用户空间和内核空间【转】

本文转载自:http://blog.csdn.net/yusiguyuan/article/details/12045255 关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用户空间与内核空间.用户空间从0到3G(0xc0000000),内核空间占据3G到4G.用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间的虚拟地址.例外情况只有用户进程进行系统调用(代表用户进程在内核态执行)等时刻可以访问到内核空间. 用户空间对应进程,所以每当进程切换,用户空间就会跟着

Linux内核源代码情景分析-内存管理之用户页面的定期换出

我们已经看到在分配页面时,如果页面数不够,那么会调用page_launder,reclaim_page,__free_page将页面换出,并重新投入分配. 为了避免总是在CPU忙碌的时候,也就是在缺页异常发生的时候,临时再来搜寻可供换出的内存页面并加以换出,Linux内核定期地检查并且预先将若干页面换出,腾出空间,以减轻系统在缺页异常发生时的负担. 为此,在Linux内核中设置了一个专司定期将页面换出的"守护神"kswapd和kreclaimd. static int __init k

Linux操作系统 内存管理、用户操作和文件操作

内存管理.用户操作和文件操作 预备知识: 1.Linux系统的内存分为物理内存和虚拟内存.物理内存是指安装在计算机当中的主存储器:虚拟内存是一段虚拟的逻辑上连续的储存空间,虚拟内存是由多个内存碎片组成,只有正在使用的虚拟内存被存放在内存上,对于暂时不使用的虚拟内存空间其实是储存在外存中.虚拟内存空间地址和实际的物理内存空间地址存在某种逻辑上的关系,如果虚拟内存空间地址的内容将被使用,通过逻辑关系可以计算出此部分内容对应的实际物理内存空间,然后将内容加载到内存中.虚拟内存在一定程度上独立于物理内存

linux内存管理

一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分:    1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程序可调用它.假如机器中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段.     2) 数据段:存放已初始化的全局变量.静态变量(包括全局和局部的).常量.static全局变量和static函数只能在当前文件中被调用.     3) 未初始化数据区(uninitializeddata s

Linux内存管理机制

一.首先大概了解一下计算机CPU.Cache.内存.硬盘之间的关系及区别. 1.  CPU也称为中央处理器(CPU,Central Processing Unit)是一块超大规模的集成电路, 是一台计算机的运算核心(Core)和控制核心( Control Unit).它的功能主要是解释计算机指令以及处理计算机软件中的数据.中央处理器主要由三核心部件组成,运算器.控制器和总线(BUS),运算器又主要由算术逻辑单元(ALU)和寄存器(RS)组成. 2.Cache即高速缓冲存储器,是位于CPU与主内存

Linux内存管理 【转】

转自:http://blog.chinaunix.net/uid-25909619-id-4491368.html Linux内存管理 摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法.力求从外到内.水到渠成地引导网友分析Linux的内存管理与使用.在本章最后,我们给出一个内存映射的实例,帮助网友们理解内核内存管理与用户内存管理之间的关系,希望大家最终能驾驭Linux内存管理. 前言 内存管理一向是所有操作系统书