pdflush内核线程

  • Post author:
  • Post category:其他




pdflush内核线程

早期版本的Linux使用bdflush内核线程系统地扫描页高速缓存以搜索要刷新的脏页,并且使用另一个内核线程kupdate来保证所有的页不会“脏”太长的时间。Linux 2.6用一组通用内核线程pdflush代替上述两个线程。

这些内核线程结构灵活,它们作用于两个参数:一个指向线程要执行的函数的指针和一 个函数要用的参数。系统中pdflush内核线程的数量是要动态调整的: pdflush 线程太少时就创建,太多时就杀死。因为这些内核线程所执行的函数可以阻塞,所以创建多个而不是一个pdflush内核线程可以改善系统性能。


根据下面的原则控制pdfllush线程的产生和消亡:

  • 必须有至少两个,最多八个pdflush内核线程。

  • 如果到最近的1s期间没有空闲pdflush,就应该创建新的pdflush.

  • 如果最近一次pdflush变为空闲的时间超过了1s,就应该删除一个pdflush.

所有的pdfush內核线程都有pdflush_work描述符。空闲pdflush内核线程的描述符都集中在paflush_list链表中,在多处理器系统中,pdflush_lock自旋锁保护该链表不会被并发访间。nr_pdflush _threads变量存放pdflush内核线程(空闲的或忙的)的总数。最后,last_empty_jifs变量存放pdflush线程的pdflush_list 链表变为空的时间(以jiffies 表示)。



pdflush_work

struct pdflush_work {
	struct task_struct *who;	/* 指向内核线程描述符的指针 */
	void (*fn)(unsigned long);	/*内核线程执行的回调函数 */
	unsigned long arg0;		/*给回调函数的参数 */
	struct list_head list;		/* pdflush_list链表的链接 */
	unsigned long when_i_went_to_sleep;//当前内核线程的可用时间
};

所有pdflush内核线程都执行函数

__pdflush()

, 它本质上循环执行一直到内核线程死亡。我们不妨假设pdflush内核线程是空闲的,而进程正在TASK_INTERRUPTIBLE状态睡眠。一但内核线程被唤醒,

__pdflush()

就访问其pdflush_work描述符,并执行字段fn中的回调函数,把arg0字段中的参数传递给该函数。当回调函数结束时,

__pdflush()

检查 last_empty_jifs变量的值:如果不存在空闲pdflush内核线程的时间已经超过1s,而且pdflush内核线程的数量不到8个,函数

__pdflush()

就创建另外 一个内核线程。相反,如果padflush_list链表中的最后一项对应的pdflush内核线程空闲时间超过了1s,而且系统中有两个以上的pdflush内核线程,函数

__pdflush()

就终止。否则,如果系统中pdflush内核线程不多于两个,

__pdflush()

就把 内核线程的pdflush_work描述符重新插人到pdflush_list链表中,并使内核线程睡眠。



pdflush

static int pdflush(void *dummy)
{
	struct pdflush_work my_work;//创建描述符

	/*
	 * pdflush can spend a lot of time doing encryption via dm-crypt.  We
	 * don't want to do that at keventd's priority.
	 */
	set_user_nice(current, 0);//设置优先级
	return __pdflush(&my_work);//执行__pdflush
}



__pdflush

static int __pdflush(struct pdflush_work *my_work)
{
	current->flags |= PF_FLUSHER;
	my_work->fn = NULL;//设置回调函数为空
	my_work->who = current;//指针指向当前
	INIT_LIST_HEAD(&my_work->list);//初始化链表头

	spin_lock_irq(&pdflush_lock);
	nr_pdflush_threads++;//内核线程数+1
	for ( ; ; ) {
		struct pdflush_work *pdf;

		set_current_state(TASK_INTERRUPTIBLE);//设置当前状态为睡眠态
		list_move(&my_work->list, &pdflush_list);//将内核线程的pdflush_work描述符插人到pdflush_list链表中
		my_work->when_i_went_to_sleep = jiffies;
		spin_unlock_irq(&pdflush_lock);

		schedule();//调用schedule让cpu处理其他的进程

		if (try_to_freeze(PF_FREEZE)) {
			spin_lock_irq(&pdflush_lock);
			continue;
		}

		spin_lock_irq(&pdflush_lock);//上锁
		if (!list_empty(&my_work->list)) {//pdflush的描述符链表为空
			printk("pdflush: bogus wakeup!\n");
			my_work->fn = NULL;
			continue;
		}
		if (my_work->fn == NULL) {//没有工作任何的回调函数
			printk("pdflush: NULL work function\n");
			continue;
		}
		spin_unlock_irq(&pdflush_lock);解锁

		(*my_work->fn)(my_work->arg0);

		/*
		 * Thread creation: For how long have there been zero
		 * available threads?
		 */
		if (jiffies - last_empty_jifs > 1 * HZ) {//到最近的1s期间内没有空闲的pdflush
			/* unlocked list_empty() test is OK here */
			if (list_empty(&pdflush_list)) {
				/* unlocked test is OK here */
				if (nr_pdflush_threads < MAX_PDFLUSH_THREADS)//内核线程pdflush总数小于8个就继续启动一个pdflush
					start_one_pdflush_thread();
			}
		}

		spin_lock_irq(&pdflush_lock);
		my_work->fn = NULL;

		/*
		 * Thread destruction: For how long has the sleepiest
		 * thread slept?
		 */
		if (list_empty(&pdflush_list))
			continue;
		if (nr_pdflush_threads <= MIN_PDFLUSH_THREADS)
			continue;
		pdf = list_entry(pdflush_list.prev, struct pdflush_work, list);
		if (jiffies - pdf->when_i_went_to_sleep > 1 * HZ) {//最近变空闲的时间超过了1s
			/* Limit exit rate */
			pdf->when_i_went_to_sleep = jiffies;
			break;					/* exeunt */
		}
	}
	nr_pdflush_threads--;//删除一个pdflush线程
	spin_unlock_irq(&pdflush_lock);
	return 0;
}



pdflush_operation

int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0)
{
	unsigned long flags;
	int ret = 0;

	if (fn == NULL)
		BUG();		/* Hard to diagnose if it's deferred */

	spin_lock_irqsave(&pdflush_lock, flags);
	if (list_empty(&pdflush_list)) {
		spin_unlock_irqrestore(&pdflush_lock, flags);
		ret = -1;
	} else {
		struct pdflush_work *pdf;

		pdf = list_entry(pdflush_list.next, struct pdflush_work, list);//从pdflush_list获取pdf指针,他指向空闲的pdflush的描述符
		list_del_init(&pdf->list);
		if (list_empty(&pdflush_list))//链表如果只剩一个元素
			last_empty_jifs = jiffies;
		pdf->fn = fn;//把参数fn赋给pdf->fn
		pdf->arg0 = arg0;//把参数arg0赋给pdf->arg0
		wake_up_process(pdf->who);//唤醒空闲的pdflush线程
		spin_unlock_irqrestore(&pdflush_lock, flags);
	}
	return ret;
}

pdflush内核线程一般做一些与脏数据刷新相关的工作。pdflush 通常执行下面的回调函数之一,它由pdflush_work->fn指向

background_writeout ():系统地扫描页高速缓存以搜索要刷新的脏页。

wb_kupdate():检查页高速缓存中是否有脏了很长时间的页。



版权声明:本文为weixin_48101150原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。