alloc_pages()函数分析

原创文章,转载请注明出处.转载自: Li Haifeng's Blog
本文链接地址: alloc_pages()函数分析

alloc_pages()函数用来给进程分配块,分配块所用的就是伙伴算法。

如果给进程分配块,调用的是get_page_from_freelist()。在分配块的过程中,我们要实施一些策略,也就是,假如可用内存的数量不足的话,要回收一些内存。我们在上一次的物理内存管理的pglist_data的数据结构也看到了,用于异步回收的进程。

在《深入理解Linux虚拟内存管理》的书中,有这样的一幅图:



这幅图中我们可以看到,在那些时候唤醒kswapd来进行异步回收内存,什么时候,同步回收内存。

其实alloc_pages()就是围绕着这幅图来设计算法的。

分配的具体步骤是:

1、用page_low作为阈值(就是分配结束后,要最少还有page_low数量的内存存在),来进行分配。如果分配成功,很好。如果分配不成功,那么就唤醒kswapd()来进行异步回收内存。

2、第一个步骤里面其实已经在某种程度上达到我们的目的了。也就是能够控制内存分配的速度了。因此,降低阈值。同时,还要看一下进程要求分配内存的分配策略,假若进程是非中断处理程序的实时进程,或者该进程不能被阻塞,那么这个时候,我要在最低阈值的标准的基础上,再次降低阈值,即page_min-=page-min/4。如果进程要求分配进程的策略还有加强,那么继续减小阈值:page_min-=page_min/2。阈值减小后,再次进行分配。

3、如果第二步分配成功了,不错;如果没有分配成功,那么就比较麻烦了。就需要启动同步回收内存的机制了。但在启动同步内存回收之前,我们要看一下进程的属性,假若进程本身是正在分配内存,或者进程正在撤销以释放内存,那么就需要特殊对待了,如果分配策略允许,就不再设置阈值,直接分配。如果没有分配成功,看一下,这个进程是不是很特殊,如果它不允许分配失败,就要不停的循环,不停的去尝试,直到分配成功。反之,分配失败,就不再往下尝试了。因为,在这里,进程本身就是回收内存,或者进程本身就在撤销以释放内存,这已经是底线了。

4、第3步,是一个特殊的情况 。那么对于一般的进程在第二步较低阈值的情况下仍然没有分配成功,就开始同步回收内存了。方法就是把进程的标志位设置为PF_MEMALLOC,然后把进程回收的状态也改变了,对于不再活跃的SLAB也给回收了。然后就开始同步回收内存了。注意,这里同步回收的话,进程实际上是被阻塞的,如果进程本身的性质不允许阻塞的话,那么在第三步实际上就OVER了。这一步的回收内存,要比异步回收内存的力度更大。将所有不活动的脏的页给写回磁盘,让他们变成干净的页,然后归入可分配的空闲页框队列,这一步需要的时间比较长。

5、如果在第4步中,我们同步回收到内存了,那么,就从重复一下第二步的分配方法,给进程分配内存。如果不成功(汗,到这里还不成功),那么同时就看两点,第一点,看分配策略是否允许重试,第二点,看进程需要分配的块为多大,如果小于等于8个页框,那么就休眠一会儿后 ,重新跳到第4步,再次同步回收内存,再次分配。否则不允许重试的话,就不要再做其他的努力了。OVER吧。

6、在第5步的假设是,同步回收内存成功了。那么如果同步回收内存没有成功了呢?这个进程十有八九是需要被KILL的。但内核采取了一种很委婉的方法,就是把阈值设为最大page_high。如果分配成功了,那运气真好;如果分配没有成功,那么名正言顺的,因为没有分配到内存,所以,就被KILL了。


909 struct page * fastcall

910 __alloc_pages(gfp_t gfp_mask, unsigned int order,

911         struct zonelist *zonelist)

912 {

913     const gfp_t wait = gfp_mask & __GFP_WAIT;//__GFP_WAIT:get_free_pages_wait

914     struct zone **z;

915     struct page *page;

916     struct reclaim_state reclaim_state;

917     struct task_struct *p = current;

918     int do_retry;

919     int alloc_flags;

920     int did_some_progress;

921

922     might_sleep_if(wait);//if wait then might_sleep

923

924 restart:

925     z = zonelist->zones;  /* the list of zones suitable for gfp_mask */

926

927     if (unlikely(*z == NULL)) {

928         /* Should this ever happen?? */

929         return NULL;

930     }

931

932     page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,

933                 zonelist, ALLOC_WMARK_LOW|ALLOC_CPUSET);

934     if (page)

935         goto got_pg;

936

937     /*

938      *以上是第一阶段,分配内存,阈值为:WMARK_LOW

939      * */

940

941     do {

942         wakeup_kswapd(*z, order);

943     } while (*(++z));

944

945     /*LLOC

946      * OK, we're below the kswapd watermark and have kicked background

947      * reclaim. Now things get more complex, so set up alloc_flags according

948      * to how we want to proceed.

949      *

950      * The caller may dip into page reserves a bit more if the caller

951      * cannot run direct reclaim, or if the caller has realtime scheduling

952      * policy or is asking for __GFP_HIGH memory.  GFP_ATOMIC requests will

953      * set both ALLOC_HARDER (!wait) and ALLOC_HIGH (__GFP_HIGH).

954      */

955     alloc_flags = ALLOC_WMARK_MIN;

956     if ((unlikely(rt_task(p)) && !in_interrupt()) || !wait)

957         alloc_flags |= ALLOC_HARDER;

958     if (gfp_mask & __GFP_HIGH)

959         alloc_flags |= ALLOC_HIGH;

960     if (wait)

961         alloc_flags |= ALLOC_CPUSET;//??

962

963     /*

964      * Go through the zonelist again. Let __GFP_HIGH and allocations

965      * coming from realtime tasks go deeper into reserves.

966      *

967      * This is the last chance, in general, before the goto nopage.

968      * Ignore cpuset if GFP_ATOMIC (!wait) rather than fail alloc.

969      * See also cpuset_zone_allowed() comment in kernel/cpuset.c.

970      */

971     page = get_page_from_freelist(gfp_mask, order, zonelist, alloc_flags);

972     if (page)

973         goto got_pg;

974

975     /*

976      *以上为第二轮的分配

977      * */

978

979     /* This allocation should allow future memory freeing. */

980

981     if (((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE)))//这两个位都是回收

982             && !in_interrupt()) {//进程是用来分配内存的

983         if (!(gfp_mask & __GFP_NOMEMALLOC)) {//GFP_NOMEMALLOC:don't use the reserved

984 nofail_alloc://may no use the reserved memory.

985             /* go through the zonelist yet again, ignoring mins */

986             page = get_page_from_freelist(gfp_mask, order,

987                 zonelist, ALLOC_NO_WATERMARKS);//Don't use the watermask

988             if (page)

989                 goto got_pg;

990             if (gfp_mask & __GFP_NOFAIL) {//if the _GFP_NOFAIL, will repeat until success

991                 blk_congestion_wait(WRITE, HZ/50);

992                 goto nofail_alloc;

993             }

994         }

995         goto nopage;//不用预留的内存的话,就到NOPAGE那个区间去

996     }

997

998     /*

999      *以上的这次分配是在该进程本身就是回收内存的进程来讲的。如果,本身不是的话,需要往下 */

1002

1003     /* Atomic allocations - we can't balance anything */

1004     if (!wait)//进程不阻塞,就nopage,如果阻塞,还以后还有机会

1005         goto nopage;

1006

1007 rebalance:

1008     cond_resched();//若允许阻塞,先让其他进程运行

1009

1010     /* We now go into synchronous reclaim */

1011     cpuset_memory_pressure_bump();//同步回收内存

1012     p->flags |= PF_MEMALLOC;

1013     reclaim_state.reclaimed_slab = 0;

1014     p->reclaim_state = &reclaim_state;

1015

1016                  /*

1017      * the follow step is to convert the inactive page to active page

1018      * */

1019     did_some_progress = try_to_free_pages(zonelist->zones, gfp_mask);

1020

1021     p->reclaim_state = NULL;

1022     p->flags &= ~PF_MEMALLOC;

1023

1024     cond_resched();

1025

1026     //1.if freepages then get

1027     //2.if no freepages and no retry then kill more

1028

1029     if (likely(did_some_progress)) {//this step is by the try_to_free_pages()

1030         page = get_page_from_freelist(gfp_mask, order,

1031                         zonelist, alloc_flags);

1032         if (page)

1033             goto got_pg;

1034     } else if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) {//don't want to retry

1035         //if try_to_free_page do nothing and don't want to try again !then ,will be killed!

1036         /*

1037          * Go through the zonelist yet one more time, keep

1038          * very high watermark here, this is only to catch

1039          * a parallel oom killing, we must fail if we're still

1040          * under heavy pressure.

1041          */

1042         page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,

1043                 zonelist, ALLOC_WMARK_HIGH|ALLOC_CPUSET);

1044         if (page)

1045             goto got_pg;

1046

1047         out_of_memory(zonelist, gfp_mask, order);//will be killed

1048         goto restart;

1049     }

1050

1051     /*

1052      * Don't let big-order allocations loop unless the caller explicitly

1053      * requests that.  Wait for some write requests to complete then retry.

1054      *

1055      * In this implementation, __GFP_REPEAT means __GFP_NOFAIL for order

1056      * <= 3, but that may not be true in other implementations.

1057      */

1058     do_retry = 0;

1059     if (!(gfp_mask & __GFP_NORETRY)) {

1060         if ((order <= 3) || (gfp_mask & __GFP_REPEAT))//根据申请order大小,Repeat

1061             do_retry = 1;

1062         if (gfp_mask & __GFP_NOFAIL)

1063             do_retry = 1;

1064     }

1065     if (do_retry) {//try again

1066         blk_congestion_wait(WRITE, HZ/50);//set the sleep time

1067         goto rebalance;//

1068     }

1069

1070 nopage:

1071     if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {

1072         printk(KERN_WARNING "%s: page allocation failure."

1073             " order:%d, mode:0x%xn",

1074             p->comm, order, gfp_mask);

1075         dump_stack();

1076         show_mem();

1077     }

1078 got_pg:

1079     return page;

1080 }

From Li Haifeng's Blog, post alloc_pages()函数分析

Post Footer automatically generated by wp-posturl plugin for wordpress.

分享到: