文章正文
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相关 阅读(2510) 评论(0)