Linux内核源代码情景分析-内存管理之用户页面的定期换出

    我们已经看到在分配页面时,如果页面数不够,那么会调用page_launder,reclaim_page,__free_page将页面换出,并重新投入分配。

    为了避免总是在CPU忙碌的时候,也就是在缺页异常发生的时候,临时再来搜寻可供换出的内存页面并加以换出,Linux内核定期地检查并且预先将若干页面换出,腾出空间,以减轻系统在缺页异常发生时的负担。

    为此,在Linux内核中设置了一个专司定期将页面换出的“守护神”kswapd和kreclaimd。

static int __init kswapd_init(void)
{
	printk("Starting kswapd v1.8\n");
	swap_setup();
	kernel_thread(kswapd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
	kernel_thread(kreclaimd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
	return 0;
}
    启动了两个内核线程,kswapd和kreclaimd。


    首先分析kswapd,代码如下:

int kswapd(void *unused)
{
	struct task_struct *tsk = current;

	tsk->session =1;
	tsk->pgrp = 1;
	strcpy(tsk->comm, "kswapd");
	sigfillset(&tsk->blocked);
	kswapd_task = tsk;
	
	.....
	tsk->flags |= PF_MEMALLOC;//执行公务,标志位置1

	/*
	 * Kswapd main loop.
	 */
	for (;;) {
		static int recalc = 0;

		/* If needed, try to free some memory. */
		if (inactive_shortage() || free_shortage()) {
			int wait = 0;
			/* Do we need to do some synchronous flushing? */
			if (waitqueue_active(&kswapd_done))
				wait = 1;
			do_try_to_free_pages(GFP_KSWAPD, wait);//主体函数
		}

		......
		refill_inactive_scan(6, 0);

		......

		......
		if (!free_shortage() || !inactive_shortage()) {
			interruptible_sleep_on_timeout(&kswapd_wait, HZ);//每隔1秒钟唤醒一次,继续执行循环
		......
		} else if (out_of_memory()) {
			oom_kill();
		}
	}
}

    在一些简单的初始化操作以后,程序便进入一个无限循环。在每次循环的末尾一般都会调用interruptible_sleep_on_timeout()进入睡眠,让内核自由地调度别的进程运行。但是内核在1秒钟后又会唤醒并调度kswapd继续运行,这时候kswapd就又回到这无限循环开始的地方。


    这个函数执行的主体函数是do_try_to_free_pages,代码如下:

static int do_try_to_free_pages(unsigned int gfp_mask, int user)
{
	int ret = 0;

	......
	if (free_shortage() || nr_inactive_dirty_pages > nr_free_pages() +
			nr_inactive_clean_pages())
		ret += page_launder(gfp_mask, user);

	......
	if (free_shortage() || inactive_shortage()) {
		shrink_dcache_memory(6, gfp_mask);
		shrink_icache_memory(6, gfp_mask);
		ret += refill_inactive(gfp_mask, user);
	} else {
		......
		kmem_cache_reap(gfp_mask);
		ret = 1;
	}

	return ret;
}
    shrink_dcache_memory和shrink_icache_memory用来回收积累起来的大量的dentry数据结构和inode数据结构。这些数据结构在文件关闭以后并不立即释放,而是放在LRU队列中作为后备,以防不久将来的文件操作又要用到。

    kmem_cache_reap用于收割slab块。slab管理机制也是倾向于分配和保持更多的空闲物理页面,而不热衷于退还这些页面,所以过一段时间就要通过kmem_cache_reap来收割。

   

    一、我们首先分析的是refill_inactive,代码如下:

static int refill_inactive(unsigned int gfp_mask, int user)
{
	int priority, count, start_count, made_progress;

	count = inactive_shortage() + free_shortage();
	if (user)
		count = (1 << page_cluster);
	start_count = count;

	/* Always trim SLAB caches when memory gets low. */
	kmem_cache_reap(gfp_mask);//收割slab

	priority = 6;//循环从优先级最低的6级开始,逐步加大"力度"直到0级,
	do {
		made_progress = 0;

		if (current->need_resched) {//内核线程必须自律,因为永远不会返回用户空间,就永远不会检查这个标志位
			__set_current_state(TASK_RUNNING);//表示希望继续执行的愿望
			schedule();//调度
		}

		while (refill_inactive_scan(priority, 1)) {
			made_progress = 1;
			if (--count <= 0)//达到目标,就提前结束
				goto done;
		}

		......
		shrink_dcache_memory(priority, gfp_mask);//回收积累起来的大量的dentry数据结构和inode数据结构
		shrink_icache_memory(priority, gfp_mask);

		......
		while (swap_out(priority, gfp_mask)) {
			made_progress = 1;
			if (--count <= 0)//达到目标,就提前结束
				goto done;
		}

		......
		if (!inactive_shortage() || !free_shortage())
			goto done;//不缺少页面了,也提前结束

		......
		if (!made_progress)
			priority--;
	} while (priority >= 0);

	/* Always end on a refill_inactive.., may sleep... */
	while (refill_inactive_scan(0, 1)) {
		if (--count <= 0)
			goto done;
	}

done:
	return (count < start_count);
}

    1、refill_inactive_scan函数,如下:

int refill_inactive_scan(unsigned int priority, int oneshot)
{
	struct list_head * page_lru;
	struct page * page;
	int maxscan, page_active = 0;
	int ret = 0;

	/* Take the lock while messing with the list... */
	spin_lock
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值