autojs遍历当前页面所有控件_伙伴系统:页面分配器
概述:
本篇主要是對伙伴系統的頁面分配器的實現進行一個梳理。在內核中伙伴系統算得上內存管理的一個基石了,畢竟頁面的分配全權由它負責,即使是slab分配器也是在它的基礎上進行實現的。頁面分配器的函數在內核中有著各種各樣的版本,不論是返回虛擬地址的還是返回struct page指針的,最終都會調用一個共同的接口:__alloc_pages_nodemask()
頁面分配API:
以下是幾個常見的頁面分配函數,可以看到最終都會調用__alloc_pages_nodemask()
__alloc_pages_node /*返回struct page的指針*/__alloc_pages__alloc_pages_nodemaskalloc_pages /*返回struct page的指針*/alloc_pages_current__alloc_pages_nodemask__get_free_pages /*返回頁面的虛擬地址*/__get_free_pagesalloc_pagesalloc_pages_current__alloc_pages_nodemask核心梳理__alloc_pages_nodemask():
上面我們看到__alloc_pages_nodemask()即頁面分配器的'心臟'了,接下來我們就梳理下這顆'心臟'中都具體做了哪些事情,主要有三步:
__alloc_pages_nodemaskprepare alloc_context //1.準備參數get_page_from_freelist //2.快路徑嘗試分配內存 __alloc_pages_slowpath //3.慢路徑嘗試分配內存細節如下:
由于分配過程很復雜,會涉及到大量的判斷,以及numa node和zone的選擇,所以會根據用戶傳參、進程配置(process flags)以及系統配置(mem policy或cgroup等)去整理一些參數,通過這些參數來控制后續內存分配和內存回收的行為。
1. 參數整理:
對__alloc_pages_nodemask()中的形參進行處理,整合出三個關鍵變量alloc_mask、alloc_flag以及struct alloc_context。(第一次整理后參數給fasthpath使用,若fastpath分配失敗,進入slowpath前還會微調這些參數)
alloc_mask:存放處理后的gfp_mask。處理主要包括:1.過濾無效的mask;2.根據是否開啟cpuset添加__GFP_HARDWALL;3.若請求發生在進程中,繼承任務的GFP_FS/IO的行為。alloc_flag:存放一些功能行為的mask。功能行為包括:1.cpuset是否開啟;2.watermark的水位線用哪根。3.請求發生在進程中,繼承任務的PF_MEMALLOC_xx的行為。alloc_context:存放了關于從哪獲取pageblock的參數。參數主要包括:zonelist、nodemask和preferred_zoneref2. 快路徑(fastpath)分配:
get_page_from_freelist //從preferred zonelist中分pagezone_watermark_fast/ok //檢測水位,具體是min low high由ALLOC_WMARK_xx決定node_reclaim //wmark不ok,就根據回收模式判斷是否進行回收rmqueue //wmark很ok,分配pagermqueue_pcplist //order為0時,從pcp中分配__rmqueue_smallest //order大于0時,從free_area[]中分配快路徑總結:
(整體進入for循環,遍歷嘗試zonelist中的zone直至分配內存成功或失敗,for循環中的處理如下)
1. 參數檢查,若有不滿足,直接continue跳過當前zone;
2. 檢查水位zone_watermark_fast/ok。其中high low min水位線用哪根兒具體由alloc_flags中的ALLOC_WMARK_xx標志決定;
3. 若水位不ok,則根據回收模式zone_reclaim_mode的設置,判斷是回收或是跳過當前zone,倘若最終沒一個zone是ok的,則快路徑失敗,進入慢路徑slowpath。
4. 若水位很ok,都符合了的話則rmqueue函數嘗試分配頁面,其中會區分order-0和非order-0的情況。
watermark的檢測:
首先wmark_pages()會根據alloc_flags中設置的是min或low或high去算出該zone的watermark是多少;然后將該watermark傳入zone_watermark_ok()判斷該zone的free pages是否滿足該水線。(檢查過程會根據內存分配的緊急程度放寬watermark)
(詳見章節: 內存回收(一):watermark與lowmem_reserve)。
核心函數rmqueue():
get_page_from_freelist()函數中做了一系列檢查,都是為了最終能調用rmqueue()函數從zone中拿到合適order的內存。細看上面的框圖,我們可以發現rmqeue()中根據order是否為0會分別調用rmqueue_pcplist()或__rmqueue_smallest(),原因如下:
- order=0:
內核中將order-0的請求和大于order-0的請求在處理上做了區分。現在的處理器動不動就十幾個核,而zone就那么幾個,當多個核要同時訪問同一個zone的時候,不免要在zone的鎖的競爭上耗費大量時間。社區開發者發現系統中對order-0的請求在內核中出現的頻次極高,且order-0所占內存僅一個頁的大小,于是就實現了per cpu的"內存池",用來滿足order-0頁面的分配,這樣就在一定程度上緩解了伙伴系統在zone的鎖上面的競爭。
static struct page *rmqueue_pcplist(...) {/*關閉本地中斷并保存中斷狀態(因為中斷上下文也可以分配內存)*/local_irq_save(flags);/*獲取當前CPU上目標zone中的per_cpu_pages指針*/pcp = &this_cpu_ptr(zone->pageset)->pcp;/*獲取per_cpu_pages中制定遷移類型的頁面list*/list = &pcp->lists[migratetype];/*從鏈表上摘取目標頁面*/page = __rmqueue_pcplist(zone, migratetype, alloc_flags, pcp, list);/*若分配成功,更新當前zone的統計信息*/if (page) {__count_zid_vm_events(PGALLOC, page_zonenum(page), 1 << order);zone_statistics(preferred_zone, zone);}/*恢復中斷*/local_irq_restore(flags);return page; }- order>0:
在__rmqueue_smallest()中從小到大循環遍歷各個order的free_list鏈表,直到使用get_page_from_free_area()成功從鏈表上摘取到最小且合適(order和migratetype都合適)的pageblock
__rmqueue_smallest()get_page_from_free_area()3. 慢路徑(slowpath)分配
__alloc_pages_slowpathwake_all_kswapds //首先喚醒所有kswapd線程,并確保slowpath過程中一直是醒著的get_page_from_freelist //調整一下alloc_flags,嘗試分配get_page_from_freelist //調整一下alloc_flags和zonelist,再試下分配__alloc_pages_direct_reclaim //reclaim后再嘗試分配,屬于同步回收__alloc_pages_direct_compact //compact后再嘗試分配,屬于同步回收__alloc_pages_may_oom //反復嘗試reclaim和compact后仍不成功,則oom殺進程后再嘗試分配,屬于同步回收慢路徑總結:
快路徑(fastpath)檢查了各個zone的low watermark,若所有zone的內存水位線都低于low,則失敗并進入慢路徑(slowpath)。
1. 慢路徑第一件事就是先喚醒所有的kswapd內核線程展開異步回收。(異步回收)
2. 由于在進入慢路徑時會對alloc_flags進行調整,且已經開啟了異步回收,再次嘗試分配或許會成功。
3. 再次調整alloc_flags以及zonelist后嘗試分配或許會成功。
4. 調用__alloc_pages_direct_reclaim(),其中會先進行直接內存回收,然后嘗試分配內存。(同步回收)
5. 調用__alloc_pages_direct_compact(),其中會進行內存壓縮(或內存規整),掃描free_area[]對碎片化的內存進行整理,然后嘗試分配內存。(同步回收)
6. 調用__alloc_pages_may_oom()觸發OOM,殺掉得分最高的進程,然后嘗試分配內存。(同步回收)
7. 整個過程會根據實際情況可能循環嘗試3、4、5三個步驟,或循環嘗試3、4、5、6四個步驟。
4. slowpath中的各種回收機制
下面梳理了slowpath中四種回收機制的調用關系,細節待補充。
4.1 slowpath中的kswapd(異步回收)
創建kswapd: kernel啟動時會調用kswapd_init()為每個NUMA node都創建一個kswapd內核線程。
kswapd_initkswapd_runkthread_run(kswapd, ..)喚醒kswapd: __alloc_pages_nodemask()分配過程中,當watermark低于low時會喚醒kswapd進行異步回收。
wake_all_kswapdswakeup_kswapd4.2 slowpath中的reclaim(同步回收)
__alloc_pages_direct_reclaim__perform_reclaimtry_to_free_pagesdo_try_to_free_pagesshrink_zonesget_page_from_freelist4.3 slowpath中的compact(同步回收)
__alloc_pages_direct_reclaim__perform_reclaimtry_to_free_pagesdo_try_to_free_pagesshrink_zonesget_page_from_freelist4.4 slowpath中的OOM(同步回收)
__alloc_pages_may_oomout_of_memoryoom_kill_process原創文章,轉載和引用請注明出處。
作者:Yann Xu
總結
以上是生活随笔為你收集整理的autojs遍历当前页面所有控件_伙伴系统:页面分配器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php实现加密解密,PHP实现的加密解密
- 下一篇: Apache Geronimo 监控