在linux内核当中,分配内存是常有的事情,许多的内核数据结构都需要动态建立,这就需要分配内存,如果当下没有可用内存的话,内存分配函数是返回 NULL,还是睡眠等待呢?这其实是两种策略,答案也是非常简单,当当前的执行环境不允许睡眠的时候就不能睡眠,比如说中断,当前可以睡眠的时候就可以睡眠等待,比如进程的系统调用或缺页异常处理中,基于以上不同的策略,内核专门为内存分配函数提供了flag参数,它们都是以GFP_打头的参数,可以参考 内核代码。最终都要进入__alloc_pages:
struct page * fastcall __alloc_pages(unsigned int gfp_mask, unsigned int order, struct zonelist *zonelist)
{
const int wait = gfp_mask & __GFP_WAIT;
unsigned long min;
struct zone **zones, *z;
struct page *page;
struct reclaim_state reclaim_state;
struct task_struct *p = current;
int i;
int alloc_type;
int do_retry;
int can_try_harder; //这个can_try_harder很重要,见下面初始化
might_sleep_if(wait);
can_try_harder = (unlikely(rt_task(p)) && !in_interrupt()) || !wait;
zones = zonelist->zones; /* the list of zones suitable for gfp_mask */
if (unlikely(zones[0] == NULL)) {
return NULL;
}
alloc_type = zone_idx(zones[0]);
for (i = 0; (z = zones[i]) != NULL; i++) {
min = z->pages_low + (1protection[alloc_type];
if (z->free_pages
continue;
page = buffered_rmqueue(z, order, gfp_mask);
if (page)
goto got_pg;
}
for (i = 0; (z = zones[i]) != NULL; i++)
wakeup_kswapd(z); //这个wakeup并不能引起进程切换,稍后解释
for (i = 0; (z = zones[i]) != NULL; i++) { //常规分配,逐渐加大强度
min = z->pages_min;
if (gfp_mask & __GFP_HIGH)
min /= 2;
if (can_try_harder) //can_try_harder影响着内存分配是否在本zone进行
min -= min / 4;
min += (1protection[alloc_type];
if (z->free_pages
continue;
page = buffered_rmqueue(z, order, gfp_mask);
if (page)
goto got_pg;
}
if ((p->flags & (PF_MEMALLOC | PF_MEMDIE)) && !in_interrupt()) {//特权分配,没有强度限制
for (i = 0; (z = zones[i]) != NULL; i++) {
page = buffered_rmqueue(z, order, gfp_mask);
if (page)
goto got_pg;
}
goto nopage;
}
if (!wait) //如果不能睡眠等待,比如在中断中,则直接退出此次分配
goto nopage;
rebalance: //平衡内存
p->flags |= PF_MEMALLOC;
reclaim_state.reclaimed_slab = 0;
p->reclaim_state = &reclaim_state;
try_to_free_pages(zones, gfp_mask, order); //这个函数中有显式的睡眠
p->reclaim_state = NULL;
p->flags &= ~PF_MEMALLOC;
for (i = 0; (z = zones[i]) != NULL; i++) {
min = z->pages_min;
if (gfp_mask & __GFP_HIGH)
min /= 2;
if (can_try_harder)
min -= min / 4;
min += (1protection[alloc_type];
if (z->free_pages
continue;
page = buffered_rmqueue(z, order, gfp_mask);
if (page)
goto got_pg;
}
do_retry = 0;
if (!(gfp_mask & __GFP_NORETRY)) {
if ((order
do_retry = 1;
if (gfp_mask & __GFP_NOFAIL)
do_retry = 1;
}
if (do_retry) {
blk_congestion_wait(WRITE, HZ/50);
goto rebalance;
}
nopage:
if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {
printk(KERN_WARNING "%s: page allocation failure."
" order:%d, mode:0x%x/n",
p->comm, order, gfp_mask);
dump_stack();
}
return NULL;
got_pg:
zone_statistics(zonelist, z);
kernel_map_pages(page, 1
return page;
}
上 述函数中有wakeup_kswapd调用,不管能否睡眠都回调用它,如果你认为它会导致进程切换会导致内存分配进程的睡眠,那么你就大错特错了,wakeup操作只是设置了TF_NEED_RESCHED标志,虽然有了调度请求,可以调度点的验证却无法通过,在中断或原子上下文,进程的 preempt标志为非0,而只有它为0的时候才会通过调度点的验证实际发生进程切换,实际上在中断中会有很多wakeup的发生,很多进程都是在中断中 被wakup的,真正会发生睡眠的是在try_to_free_pages函数中,该函数中可能要调用blk_congestion_wait,而 blk_congestion_wait则会毫不犹豫地进入睡眠,因此页面分配标志中如果没有_GFP_WAIT标志,根本就无法进入 try_to_free_pages,从而也不会睡眠,反之,一旦设置了该标志便会有可能进入睡眠。我们来看看vmalloc函数会不会睡眠:
void *vmalloc(unsigned long size)
{
return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
}
#define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS)
结果很显然,我就不说了,而kmalloc和get_free_pages是可以自己设置标志把握策略的,因此在中断中不要调用vmalloc来分配内存。
分享到:
相关推荐
Linux内核Slub内存分配机制分析,李英鹏,,Slab内存分配机制是Linux内核采用的传统内存分配机制。随着系统的发展,它暴露出队列管理复杂,管理结构内存消耗大,回收机制比较复
阐述了linux内核内存的分配方式和接口函数,希望对想学习内核的同学有所帮助。
linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测
详细介绍了slab的源代码 和原理 对掌握内核内存管理很大帮助 支持2.6版本的
Linux内核内存管理,很全的内存管理讲解,自己手动整理的,绝对原创。
深入理解Linux内核-内存篇,阅读笔记
鉴于此,《Linux内核精髓:精通Linux内核必会的75个绝技》选取了资源管理(CPU、内存、进程等)、文件系统、网络、虚拟化、省电、调试、概要分析、追踪、内核调整等Linux内核的核心主题进行了深入剖析和讲解,总结出...
linux内核编译2.6.39linux内核编译2.6.39linux内核编译2.6.39linux内核编译2.6.39linux内核编译2.6.39linux内核编译2.6.39linux内核编译2.6.39linux内核编译2.6.39linux内核编译2.6.39linux内核编译2.6.39linux内核...
linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测
linux开发者介绍linux内存管理的机制,图文并茂,细致入里
Linux内核分析-内存篇,不懂,就多看看吧。~~~~~~~~~
嵌入式开发教程之Linux内核中常见内存分配函数(“内存”文档)共18张.ppt
linux内核设计说明,Linux内核设计与实现(第三版中文高清带目录)
理解Linux内核最好预备的知识点:懂C语言懂一点操作系统的知识熟悉少量相关算法懂计算机体系结构Linux内核的特点:结合了unix操作系统的一些基础概念Linux内核的任务:1.从技术层面讲,内核是硬件与软
Linux内核中的内存访问监控框架的设计与实现.pdf
linux内核源代码深度解析linux内核源代码深度解析linux内核源代码深度解析linux内核源代码深度解析linux内核源代码深度解析linux内核源代码深度解析linux内核源代码深度解析linux内核源代码深度解析linux内核源代码...
22) Linux中的浮点运算由应用程序实现还是内核实现? 23) 模块程序能否使用可链接的库函数? 24) TLB中缓存的是什么内容? 25) Linux中有哪几种设备? 26) 字符设备驱动程序的关键数据结构是哪个? 27) 设备驱动程序...
Linux内核的内存管理探秘之四 虚拟内存的管理.pdf
深入理解Linux内核 + Linux内核设计与实现,绝对完整,我最近也在学,建议先学Linux内核设计与实现,对Linux内核有一个大体的认识,在看深入理解Linux内核,要舍得花时间。
Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究