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. 编译链接的一些知识
首先,我们来编写一个简单的程序,示例代码如下
- #include "stdio.h"
- #include "string.h"
- #include "stdlib.h"
- int i=3;
- int j=4;
- int main()
- {
- 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);
- }
程序片段1
程序片段1定义了两个变量,a和b,并分别赋值为3,4。然后在主函数main中,打印了i和j的值及其对其对应的虚拟地址。程序的运行结果如下:
- value i is 3
- address of i is 0x80495e0,
- address of j is 0x80495e4
片段1
程序片段2中所示的0x80495e0,0x80495e4分别是i和j的虚拟地址,那么,这个地址是怎么确定的?由谁来确定?
通过查看编译后的可执行程序的符号列表,i和j的地址实际上是在链接的过程中确定的,由编译器来确定。
- objdump -t a.out
- a.out: file format elf32-i386
- SYMBOL TABLE:
- ......
- 00000000 w *UND* 00000000 _Jv_RegisterClasses
- 08048494 g O .rodata 00000004 _fp_hw
- 08048478 g F .fini 00000000 _fini
- 00000000 F *UND* 0000019f __libc_start_main@@GLIBC_2.0
- 08048498 g O .rodata 00000004 _IO_stdin_used
- 080495dc g .data 00000000 __data_start
- 080495e0 g O .data 00000004 i
- 0804849c g O .rodata 00000000 .hidden __dso_handle
- 080494f0 g O .dtors 00000000 .hidden __DTOR_END__
- 080483e0 g F .text 00000069 __libc_csu_init
- 00000000 F *UND* 00000039 printf@@GLIBC_2.0
- 080495e8 g *ABS* 00000000 __bss_start
- 080495e4 g O .data 00000004 j
- 080495f0 g *ABS* 00000000 _end
- 080495e8 g *ABS* 00000000 _edata
- 08048449 g F .text 00000000 .hidden __i686.get_pc_thunk.bx
- 08048384 g F .text 00000044 main
- 08048250 g F .init 00000000 _init
2. 管理用户空间进程内存的结构体
当一个程序被载入内存,内核就会为其建立一个名为task_struct的结构体来管理这个进程。
- struct task_struct {
- struct list_head tasks;
- struct mm_struct *mm, *active_mm;//字段mm为该进程的内存管理。
- pid_t pid;
- struct fs_struct *fs;
- struct files_struct *files;
- ......
- };
task_struct
- struct mm_struct {
- struct vm_area_struct * mmap; /* list of VMAs ,一个进程有一个mm_struct,多个
- vm_area_struct*/
- pgd_t * pgd;
- int map_count; /* number of VMAs */
- ......
- };
mm_struct
- struct vm_area_struct {
- struct mm_struct * vm_mm; /* The address space we belong to. */
- unsigned long vm_start; /* Our start address within vm_mm. */
- unsigned long vm_end; /* The first byte after our end address
- within vm_mm. */
- }
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这个系统调用,将执行的状态从用户空间转向内核空间。
linux内存管理-内核用户空间 【转】的更多相关文章
- [转帖]linux 内存管理——内核的shmall 和shmmax 参数
(转)linux 内存管理——内核的shmall 和shmmax 参数 内核的 shmall 和 shmmax 参数 SHMMAX= 配置了最大的内存segment的大小 ------>这个 ...
- linux 内存管理——内核的shmall 和shmmax 参数
内核的 shmall 和 shmmax 参数 SHMMAX= 配置了最大的内存segment的大小 ------>这个设置的比SGA_MAX_SIZE大比较好. SHMMIN= 最小的内存seg ...
- (转)linux 内存管理——内核的shmall 和shmmax 参数
内核的 shmall 和 shmmax 参数 SHMMAX= 配置了最大的内存segment的大小 ------>这个设置的比SGA_MAX_SIZE大比较好. SHMMIN= 最小的内存seg ...
- Linux内存管理-内核的shmall和shmmax参数(性能调优)(转)
内核的shmall和shmmax参数 SHMMAX=配置了最大的内存segment的大小:这个设置的比SGA_MAX_SIZE大比较好. SHMMIN=最小的内存segment的大小 SHMMNI=整 ...
- Linux内存管理 —— 内核态和用户态的内存分配方式
1. 使用buddy系统管理ZONE我的这两篇文章buddy系统和slab分配器已经分析过buddy和slab的原理和源码,因此一些细节不再赘述.所有zone都是通过buddy系统管理的,buddy ...
- Linux内存管理解析(二) : 关于Linux内存管理的大体框架
什么是内存管理 ? 首先内存管理管理的主要对象是虚拟内存,但是虚拟内存对应的映射主要为物理内存,其次也可能通过交换空间把虚拟内存与硬盘映射起来,既然如此,那我们先了解物理内存的管理. 对于物理内存而言 ...
- Linux内存管理--用户空间和内核空间【转】
本文转载自:http://blog.csdn.net/yusiguyuan/article/details/12045255 关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用 ...
- linux内存管理--用户空间和内核空间
关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用户空间与内核空间.用户空间从0到3G(0xc0000000),内核空间占据3G到4G.用户进程通常情况下只能访问用户空间的虚拟 ...
- Linux内核分析(三)----初识linux内存管理子系统
原文:Linux内核分析(三)----初识linux内存管理子系统 Linux内核分析(三) 昨天我们对内核模块进行了简单的分析,今天为了让我们今后的分析没有太多障碍,我们今天先简单的分析一下linu ...
随机推荐
- c++实现计算器功能 -----初代
由于时间问题,我就写的简单一点. 课程作业一 git链接: Operations 里面的Operations.cpp文件就是完成品. 1 我就简单的对我原来的代码进行了重构,原本的代码已经把函数都分得 ...
- 重温redis命令
redis是已知的性能最快的key-value 数据库. 1.key相关命令 exists key :检查指定的key是否存在 1表示存在 0表示不存在 del key1,key2,key3....: ...
- 11th 回顾5个问题
当初提出的5个问题: 1.书中说很多非常成功的软件都是赢在用户体验,后面的第12章也专门提到了用户体验,说软件开发时可以使用5W1H的方法来判断用户的体验,而需求分析需要获取用户需求,进行用户调研,那 ...
- ehcache、redis应用场景比较
应用场景: ehcache是Hibernate中默认的CacheProvider,直接在jvm虚拟机中缓存,速度快,效率高:但是缓存共享麻烦,集群分布式应用不方便. . 缓存数据有两级:内存和磁盘, ...
- REQUIRES_NEW 如果不在一个事务那么自己创建一个事务 如果在一个事务中 自己在这个大事务里面在创建一个子事务 相当于嵌套事务 双层循环那种
REQUIRES_NEW 如果不在一个事务那么自己创建一个事务 如果在一个事务中 自己在这个大事务里面在创建一个子事务 相当于嵌套事务 双层循环那种 不管是否存在事务,业务方法总会自己开启一个事 ...
- RK哈希(Rabin_Karp 哈希)
Rabin_Karp 哈希通过比较hash值是否相等来比较每个字符串是否相等有概率出错(很小)字符串x1,x2,x3……xk基底e;模数mo;hash=(xk*e^0+xk-1*e^1+......+ ...
- 洛谷P1155 双栈排序
这题什么毒瘤......之前看一直没思路,然后心说写个暴搜看能有多少分,然后就A了??! 题意:给你一个n排列,求它们能不能通过双栈来完成排序.如果能输出最小字典序方案. [update]这里面加了一 ...
- HDU 6085 bitset
Rikka with Candies Time Limit: 7000/3500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Other ...
- 127. Word Ladder(M)
127. Word LadderGiven two words (beginWord and endWord), and a dictionary's word list, find the leng ...
- pthread_detach
http://blog.csdn.net/scanery/article/details/7241890 感谢作者! 近来发现 在线程函数第一行调用 pthread_detach(pthread_ ...