从prev,next到list , 再到list rcu
以前自己写程序,都是自己些prev , next指针来构造成链表。难免出错,需要调试半天。从2.4开始,内核增加了list.h,这个库实现了很好的循环双向链表的功能。自己也就习惯了用list_add , list_del , list_for_each...一系列函数。使用list库的好处显而易见,不用自己操作链表指针,也不用自己些添加,删除 , 直接用很方便,加上list_entry结合,简直用的很爽,另外一个就是用了list后,减少了代码函数。
常见的list操作如何所示 :
int look_func( xxx )
{
lock section
list_for_each( pnew , phead ){
h_p = list_entry( pnew , struct xxx , member_name );
.....
}
unlock section
}
int add_func( xxxx )
{
new = kmalloc( xxx );
pnew = &(new->list);
lock section
list_add( pnew , phead );
unlock section
}
int del_func( xxxx )
{
lock section
list_del( pold );
unlock section
old = list_entry( pold , struct xxx , member );
kfree( old );
}
此处的锁由调用的点决定,在网络处理中,一般使用spin_lock , rw_lock .
从2.6内核开始,确切的说是从2.5内核开始,加入了rcu锁。 自然list.h也加入了对rcu的支持
上面的代码也就可以演变成如下:
int look_func( xxx )
{
rcu_lock( );
list_for_each_rcu( pnew , phead ){
h_p = list_entry( pnew , struct xxx , member_name );
.....
}
rcu_unlock();
}
int add_func( xxxx )
{
new = kmalloc( xxx );
pnew = &(new->list);
old lock section
list_add_rcu( pnew , phead );
old unlock section
}
int del_free( void * arg )
{
kfree( arg ); //feel free to free the memory
}
int del_func( xxxx )
{
lock section
list_del_rcu( pold );
unlock section
old = list_entry( pold , struct xxx , member );
call_rcu( xxx , del_free , old ); //注册回掉函数del_free
}
在rcu版本中, old lock的锁是否需要取决于add函数和del函数是否会同时运行,如果绝对串行,old lock func就可以不需要。
rcu的引入,是的look_func中没有锁,rcu_lock()也仅仅是关闭内核的抢占功能。rcu的最基本的原理就是在一个没有任何内核running_routine再会对这会数据引用的时候,去释放他。当然这个要求任何其他地方不能有指针指向这会数据,如果有这样的需求,就需要加入传统的refcnt机制了。
从2.6内核开始,很大部分代码就开始想rcu版本改动了,但是rcu并不是适合所有地方的。 就想rwlock一样,并不适合任何数据结构。使用rcu有两个前提:(1) 读的次数大大大于写的次数 (2)读端对数据的新旧并不敏感。
由于工作上还是停留在2.4内核,所以一直没有使用rcu机制,这次也是第一次尝试,如果有错误,欢迎指出。