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():检查页高速缓存中是否有脏了很长时间的页。