Linux内核内存管理( 六 )


( zone **zones, gfp_t )函数的目标是通过循环调用()和()释放至少32个页帧,每次调用增加优先级参数,初始优先级是12,最高为0 。如果循环13次,仍然没有释放掉32个页面,则PFRA进行内存异出保护:调用()函数选择一个进程回收它所有的页面 。
(int ,zone **zones,*sc)函数对zones列表中每个区调用()函数 。调用()前,先用sc->的值更新zone描述符中的,如果zone->字段不为0且优先级不是12,则不对该区进行页面回收 。
(int ,zone *zone,*sc)函数尝试回收32个页面 。该函数循环进行()和的操作以达到目标 。该函数流程如下:
atomic_inc(&zone->reclaim_in_progress)增加区的回收计数;
增加zone->nr_scan_active,根据优先级,增加范围是zone->nr_active/212 to zone->nr_active/20。如果zone->nr_scan_active >= 32则赋给nr_active变量,同时zone->nr_scan_active设为0,否则nr_active=0;
zone->nr_scan_inactive和nr_inactive做同样处理;
如果nr_active和nr_inactive不同时为空,则进行while循环进行5、6步操作:
如果nr_active非0,则从active链表移动某些页面到inactive链表:nr_to_scan = min(nr_active,(unsigned long)sc->swap_cluster_max);nr_active -= nr_to_scan;shrink_active_list(nr_to_scan, zone, sc, priority);
如果nr_inactive非0,则回收inactive链表中的页面:nr_to_scan = min(nr_inactive,(unsigned long)sc->swap_cluster_max);nr_inactive -= nr_to_scan;nr_reclaimed += shrink_inactive_list(nr_to_scan, zone, sc);
atomic_dec(&zone->reclaim_in_progress)减小回收计数,并返回回收页面数nr_reclaimed;
( long ,zone *zone,*sc)函数从区的链表中抽取一组页面,放到临时链表中,调用()对链表中的每个页面进行回收 。下面是()主要步骤:
调用lru_add_drain()将当前CPU上pagevec结构的lru_add_pvecs和lru_add_active_pvecs中的页面分别移到活动链表和非活动链表中;
获取LRU锁spin_lock_irq(&zone->lru_lock);
最多扫描max_scan个页面,对每个页面增加使用计数,检查该页面是否正被释放到伙伴系统中,将该页面移动一个临时链表中;
从zone->nr_inactive中减去移到临时链表中的页面数;
增加zone->pages_scanned计数;
释放LRU锁:spin_unlock_irq(&zone->lru_lock);
对临时链表调用shrink_page_list(&page_list, sc)回收页面;
增加nr_reclaimed计数;
获取LRU锁spin_lock(&zone->lru_lock);
将(&, sc)没有回收掉的页面重新添加到链表和链表中 。该函数在回收过程中可能会设置标志,所以也要考虑往链表中添加 。如果扫描页面数小于则循环进行3~10的操作;返回回收的页面数;
(*,*sc)做真正的页面回收工作,该函数流程如下:
图 ()页面回收逻辑处理流程
调用cond_resched()进行条件调度;
循环遍历page_list中每个页面,从列表中移出该页面描述符并回收该页面,如果回收失败,则把该页面插入一个局部链表中;该步流程参见流程图 。
l 调用() 进行条件调度;
l 从LRU链表中取出第一个页面并从LRU链表中删除;
l 如果页面被锁定,这调过该页面,该页加到临时链表中;
l 如果页面不能部分回收并且页面是进程页表的映射,这跳过该页;
l 如果进程是回写的dirty页面,则跳过;
l 如果页面被引用并且页面映射在使用,这跳过并激活该页面,以便后面放入列表;
l 如果是匿名页面且不在交换区中,这调用()为该页面分配交换区空间并把该页加到交换缓存中;