文章正文
kernel中信号量实现(一)
kernel中很多地方使用了信号量机制,一个典型的实现就是内存回收的触发,在内存进入慢速路径后的第一件事就是唤醒kswapd工作。
信号量的使用很简单,总结起来有下面几点:
1、初始化信号量。
wait_queue_head_t _wait; //变量定义 init_waitqueue_head(&_wait); //变量初始化
2、工作线程一般是一个死循环,工作结束后调用wait,等待触发线程唤醒,唤醒后处理wait事件。
while(1) {
DEFINE_WAIT(wait); //定义临时变量
prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE); //设置线程的状态
schedule(); //让出CPU
//在获取到信号后从这里运行
finish_wait(&pgdat->kswapd_wait, &wait); //唤醒后清理上次的信号
}3、触发线程在检查到触发事件后,wakeup对应的信号量。
wake_up_interruptible(&pgdat->kswapd_wait); //向工作线程发送信号唤醒
这一篇我们只讲使用不讲原理,原理看下一篇,这里我们就用kswapd作为信号量的使用的示例
1、我们来看kswapd的初始化
static int __init kswapd_init(void)
{
int nid, ret;
swap_setup();
for_each_node_state(nid, N_MEMORY)
kswapd_run(nid);
return 0;
}int kswapd_run(int nid)
{
pg_data_t *pgdat = NODE_DATA(nid);
int ret = 0;
if (pgdat->kswapd)
return 0;
pgdat->kswapd = kthread_run(kswapd, pgdat, "kswapd%d", nid);
if (IS_ERR(pgdat->kswapd)) {
/* failure at boot is fatal */
BUG_ON(system_state < SYSTEM_RUNNING);
pr_err("Failed to start kswapd on node %d\n", nid);
ret = PTR_ERR(pgdat->kswapd);
pgdat->kswapd = NULL;
}
return ret;
}在kswapd_init中,每一个Node运行了一次kswapd_run函数,在这个函数中,建立了我们所熟知的kswapd内核线程。
static int kswapd(void *p)
{
pg_data_t *pgdat = (pg_data_t*)p;
struct task_struct *tsk = current;
……
for ( ; ; ) {
……
kswapd_try_to_sleep(pgdat, alloc_order, reclaim_order,
classzone_idx);
……
}
}在启动的kswapd内核线程运行了kswapd这个方法,这个方法是一个循环体,在循环体中,通过kswapd_try_to_sleep等待到一个信号量上,下面我们看下kswapd_try_to_sleep函数的实现。
static void kswapd_try_to_sleep(pg_data_t *pgdat, int alloc_order, int reclaim_order,
unsigned int classzone_idx)
{
long remaining = 0;
DEFINE_WAIT(wait);
if (freezing(current) || kthread_should_stop())
return;
prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
……
schedule();
……
finish_wait(&pgdat->kswapd_wait, &wait);
}这个函数的实现基本就是我们上面的工作线程的流程,我们可以看到对应的等待队列wait_queue_head_t变量是
pgdat->kswapd_wait,那这个变量在哪里初始化的呢?
static void __paginginit free_area_init_core(struct pglist_data *pgdat)
{
……
init_waitqueue_head(&pgdat->kswapd_wait);
……
}简单的代码里面搜索一下就知道,这个变量在内存初始化的时候初始化的。
2、慢速路径触发kswapd
下面简单看下在kswapd wait之后是如何唤醒的,唤醒kswapd的地方不止一处,这里以内存分配时唤醒为例。
最终核心就是调用wakeup_kswapd函数
void wakeup_kswapd(struct zone *zone, int order, enum zone_type classzone_idx)
{
pg_data_t *pgdat;
if (!managed_zone(zone))
return;
……
if (!waitqueue_active(&pgdat->kswapd_wait))
return;
……
wake_up_interruptible(&pgdat->kswapd_wait);
}waitqueue_active函数用于探测kswapd函数是否已经在运行,如果没有在运行,则调用wake_up_interruptible(&pgdat->kswapd_wait);唤醒kswapd做内存回收。
Nov. 27, 2018, 1 a.m. 作者:zachary 分类:Linux相关 阅读(3521) 评论(0)