1*f1e69953SYanteng Si.. include:: ../disclaimer-zh_CN.rst 2*f1e69953SYanteng Si 3*f1e69953SYanteng Si:Original: Documentation/core-api/kref.rst 4*f1e69953SYanteng Si 5*f1e69953SYanteng Si翻译: 6*f1e69953SYanteng Si 7*f1e69953SYanteng Si司延腾 Yanteng Si <siyanteng@loongson.cn> 8*f1e69953SYanteng Si 9*f1e69953SYanteng Si校译: 10*f1e69953SYanteng Si 11*f1e69953SYanteng Si <此处请校译员签名(自愿),我将在下一个版本添加> 12*f1e69953SYanteng Si 13*f1e69953SYanteng Si.. _cn_core_api_kref.rst: 14*f1e69953SYanteng Si 15*f1e69953SYanteng Si================================= 16*f1e69953SYanteng Si为内核对象添加引用计数器(krefs) 17*f1e69953SYanteng Si================================= 18*f1e69953SYanteng Si 19*f1e69953SYanteng Si:作者: Corey Minyard <minyard@acm.org> 20*f1e69953SYanteng Si:作者: Thomas Hellstrom <thellstrom@vmware.com> 21*f1e69953SYanteng Si 22*f1e69953SYanteng Si其中很多内容都是从Greg Kroah-Hartman2004年关于krefs的OLS论文和演讲中摘 23*f1e69953SYanteng Si录的,可以在以下网址找到: 24*f1e69953SYanteng Si 25*f1e69953SYanteng Si - http://www.kroah.com/linux/talks/ols_2004_kref_paper/Reprint-Kroah-Hartman-OLS2004.pdf 26*f1e69953SYanteng Si - http://www.kroah.com/linux/talks/ols_2004_kref_talk/ 27*f1e69953SYanteng Si 28*f1e69953SYanteng Si简介 29*f1e69953SYanteng Si==== 30*f1e69953SYanteng Si 31*f1e69953SYanteng Sikrefs允许你为你的对象添加引用计数器。如果你有在多个地方使用和传递的对象, 32*f1e69953SYanteng Si而你没有refcounts,你的代码几乎肯定是坏的。如果你想要引用计数,krefs是个 33*f1e69953SYanteng Si好办法。 34*f1e69953SYanteng Si 35*f1e69953SYanteng Si要使用kref,请在你的数据结构中添加一个,如:: 36*f1e69953SYanteng Si 37*f1e69953SYanteng Si struct my_data 38*f1e69953SYanteng Si { 39*f1e69953SYanteng Si . 40*f1e69953SYanteng Si . 41*f1e69953SYanteng Si struct kref refcount; 42*f1e69953SYanteng Si . 43*f1e69953SYanteng Si . 44*f1e69953SYanteng Si }; 45*f1e69953SYanteng Si 46*f1e69953SYanteng Sikref可以出现在数据结构体中的任何地方。 47*f1e69953SYanteng Si 48*f1e69953SYanteng Si初始化 49*f1e69953SYanteng Si====== 50*f1e69953SYanteng Si 51*f1e69953SYanteng Si你必须在分配kref之后初始化它。 要做到这一点,可以这样调用kref_init:: 52*f1e69953SYanteng Si 53*f1e69953SYanteng Si struct my_data *data; 54*f1e69953SYanteng Si 55*f1e69953SYanteng Si data = kmalloc(sizeof(*data), GFP_KERNEL); 56*f1e69953SYanteng Si if (!data) 57*f1e69953SYanteng Si return -ENOMEM; 58*f1e69953SYanteng Si kref_init(&data->refcount); 59*f1e69953SYanteng Si 60*f1e69953SYanteng Si这将kref中的refcount设置为1。 61*f1e69953SYanteng Si 62*f1e69953SYanteng SiKref规则 63*f1e69953SYanteng Si======== 64*f1e69953SYanteng Si 65*f1e69953SYanteng Si一旦你有一个初始化的kref,你必须遵循以下规则: 66*f1e69953SYanteng Si 67*f1e69953SYanteng Si1) 如果你对一个指针做了一个非临时性的拷贝,特别是如果它可以被传递给另一个执 68*f1e69953SYanteng Si 行线程,你必须在传递之前用kref_get()增加refcount:: 69*f1e69953SYanteng Si 70*f1e69953SYanteng Si kref_get(&data->refcount); 71*f1e69953SYanteng Si 72*f1e69953SYanteng Si 如果你已经有了一个指向kref-ed结构体的有效指针(refcount不能为零),你 73*f1e69953SYanteng Si 可以在没有锁的情况下这样做。 74*f1e69953SYanteng Si 75*f1e69953SYanteng Si2) 当你完成对一个指针的处理时,你必须调用kref_put():: 76*f1e69953SYanteng Si 77*f1e69953SYanteng Si kref_put(&data->refcount, data_release); 78*f1e69953SYanteng Si 79*f1e69953SYanteng Si 如果这是对该指针的最后一次引用,释放程序将被调用。如果代码从来没有尝试过 80*f1e69953SYanteng Si 在没有已经持有有效指针的情况下获得一个kref-ed结构体的有效指针,那么在没 81*f1e69953SYanteng Si 有锁的情况下这样做是安全的。 82*f1e69953SYanteng Si 83*f1e69953SYanteng Si3) 如果代码试图获得对一个kref-ed结构体的引用,而不持有一个有效的指针,它必 84*f1e69953SYanteng Si 须按顺序访问,在kref_put()期间不能发生kref_get(),并且该结构体在kref_get() 85*f1e69953SYanteng Si 期间必须保持有效。 86*f1e69953SYanteng Si 87*f1e69953SYanteng Si例如,如果你分配了一些数据,然后将其传递给另一个线程来处理:: 88*f1e69953SYanteng Si 89*f1e69953SYanteng Si void data_release(struct kref *ref) 90*f1e69953SYanteng Si { 91*f1e69953SYanteng Si struct my_data *data = container_of(ref, struct my_data, refcount); 92*f1e69953SYanteng Si kfree(data); 93*f1e69953SYanteng Si } 94*f1e69953SYanteng Si 95*f1e69953SYanteng Si void more_data_handling(void *cb_data) 96*f1e69953SYanteng Si { 97*f1e69953SYanteng Si struct my_data *data = cb_data; 98*f1e69953SYanteng Si . 99*f1e69953SYanteng Si . do stuff with data here 100*f1e69953SYanteng Si . 101*f1e69953SYanteng Si kref_put(&data->refcount, data_release); 102*f1e69953SYanteng Si } 103*f1e69953SYanteng Si 104*f1e69953SYanteng Si int my_data_handler(void) 105*f1e69953SYanteng Si { 106*f1e69953SYanteng Si int rv = 0; 107*f1e69953SYanteng Si struct my_data *data; 108*f1e69953SYanteng Si struct task_struct *task; 109*f1e69953SYanteng Si data = kmalloc(sizeof(*data), GFP_KERNEL); 110*f1e69953SYanteng Si if (!data) 111*f1e69953SYanteng Si return -ENOMEM; 112*f1e69953SYanteng Si kref_init(&data->refcount); 113*f1e69953SYanteng Si 114*f1e69953SYanteng Si kref_get(&data->refcount); 115*f1e69953SYanteng Si task = kthread_run(more_data_handling, data, "more_data_handling"); 116*f1e69953SYanteng Si if (task == ERR_PTR(-ENOMEM)) { 117*f1e69953SYanteng Si rv = -ENOMEM; 118*f1e69953SYanteng Si kref_put(&data->refcount, data_release); 119*f1e69953SYanteng Si goto out; 120*f1e69953SYanteng Si } 121*f1e69953SYanteng Si 122*f1e69953SYanteng Si . 123*f1e69953SYanteng Si . do stuff with data here 124*f1e69953SYanteng Si . 125*f1e69953SYanteng Si out: 126*f1e69953SYanteng Si kref_put(&data->refcount, data_release); 127*f1e69953SYanteng Si return rv; 128*f1e69953SYanteng Si } 129*f1e69953SYanteng Si 130*f1e69953SYanteng Si这样,两个线程处理数据的顺序并不重要,kref_put()处理知道数据不再被引用并释 131*f1e69953SYanteng Si放它。kref_get()不需要锁,因为我们已经有了一个有效的指针,我们拥有一个 132*f1e69953SYanteng Sirefcount。put不需要锁,因为没有任何东西试图在没有持有指针的情况下获取数据。 133*f1e69953SYanteng Si 134*f1e69953SYanteng Si在上面的例子中,kref_put()在成功和错误路径中都会被调用2次。这是必要的,因 135*f1e69953SYanteng Si为引用计数被kref_init()和kref_get()递增了2次。 136*f1e69953SYanteng Si 137*f1e69953SYanteng Si请注意,规则1中的 "before "是非常重要的。你不应该做类似于:: 138*f1e69953SYanteng Si 139*f1e69953SYanteng Si task = kthread_run(more_data_handling, data, "more_data_handling"); 140*f1e69953SYanteng Si if (task == ERR_PTR(-ENOMEM)) { 141*f1e69953SYanteng Si rv = -ENOMEM; 142*f1e69953SYanteng Si goto out; 143*f1e69953SYanteng Si } else 144*f1e69953SYanteng Si /* BAD BAD BAD - 在交接后得到 */ 145*f1e69953SYanteng Si kref_get(&data->refcount); 146*f1e69953SYanteng Si 147*f1e69953SYanteng Si不要以为你知道自己在做什么而使用上述构造。首先,你可能不知道自己在做什么。 148*f1e69953SYanteng Si其次,你可能知道自己在做什么(有些情况下涉及到锁,上述做法可能是合法的), 149*f1e69953SYanteng Si但其他不知道自己在做什么的人可能会改变代码或复制代码。这是很危险的作风。请 150*f1e69953SYanteng Si不要这样做。 151*f1e69953SYanteng Si 152*f1e69953SYanteng Si在有些情况下,你可以优化get和put。例如,如果你已经完成了一个对象,并且给其 153*f1e69953SYanteng Si他对象排队,或者把它传递给其他对象,那么就没有理由先做一个get,然后再做一个 154*f1e69953SYanteng Siput:: 155*f1e69953SYanteng Si 156*f1e69953SYanteng Si /* 糟糕的额外获取(get)和输出(put) */ 157*f1e69953SYanteng Si kref_get(&obj->ref); 158*f1e69953SYanteng Si enqueue(obj); 159*f1e69953SYanteng Si kref_put(&obj->ref, obj_cleanup); 160*f1e69953SYanteng Si 161*f1e69953SYanteng Si只要做enqueue就可以了。 我们随时欢迎对这个问题的评论:: 162*f1e69953SYanteng Si 163*f1e69953SYanteng Si enqueue(obj); 164*f1e69953SYanteng Si /* 我们已经完成了对obj的处理,所以我们把我们的refcount传给了队列。 165*f1e69953SYanteng Si 在这之后不要再碰obj了! */ 166*f1e69953SYanteng Si 167*f1e69953SYanteng Si最后一条规则(规则3)是最难处理的一条。例如,你有一个每个项目都被krefed的列表, 168*f1e69953SYanteng Si而你希望得到第一个项目。你不能只是从列表中抽出第一个项目,然后kref_get()它。 169*f1e69953SYanteng Si这违反了规则3,因为你还没有持有一个有效的指针。你必须添加一个mutex(或其他锁)。 170*f1e69953SYanteng Si比如说:: 171*f1e69953SYanteng Si 172*f1e69953SYanteng Si static DEFINE_MUTEX(mutex); 173*f1e69953SYanteng Si static LIST_HEAD(q); 174*f1e69953SYanteng Si struct my_data 175*f1e69953SYanteng Si { 176*f1e69953SYanteng Si struct kref refcount; 177*f1e69953SYanteng Si struct list_head link; 178*f1e69953SYanteng Si }; 179*f1e69953SYanteng Si 180*f1e69953SYanteng Si static struct my_data *get_entry() 181*f1e69953SYanteng Si { 182*f1e69953SYanteng Si struct my_data *entry = NULL; 183*f1e69953SYanteng Si mutex_lock(&mutex); 184*f1e69953SYanteng Si if (!list_empty(&q)) { 185*f1e69953SYanteng Si entry = container_of(q.next, struct my_data, link); 186*f1e69953SYanteng Si kref_get(&entry->refcount); 187*f1e69953SYanteng Si } 188*f1e69953SYanteng Si mutex_unlock(&mutex); 189*f1e69953SYanteng Si return entry; 190*f1e69953SYanteng Si } 191*f1e69953SYanteng Si 192*f1e69953SYanteng Si static void release_entry(struct kref *ref) 193*f1e69953SYanteng Si { 194*f1e69953SYanteng Si struct my_data *entry = container_of(ref, struct my_data, refcount); 195*f1e69953SYanteng Si 196*f1e69953SYanteng Si list_del(&entry->link); 197*f1e69953SYanteng Si kfree(entry); 198*f1e69953SYanteng Si } 199*f1e69953SYanteng Si 200*f1e69953SYanteng Si static void put_entry(struct my_data *entry) 201*f1e69953SYanteng Si { 202*f1e69953SYanteng Si mutex_lock(&mutex); 203*f1e69953SYanteng Si kref_put(&entry->refcount, release_entry); 204*f1e69953SYanteng Si mutex_unlock(&mutex); 205*f1e69953SYanteng Si } 206*f1e69953SYanteng Si 207*f1e69953SYanteng Si如果你不想在整个释放操作过程中持有锁,kref_put()的返回值是有用的。假设你不想在 208*f1e69953SYanteng Si上面的例子中在持有锁的情况下调用kfree()(因为这样做有点无意义)。你可以使用kref_put(), 209*f1e69953SYanteng Si如下所示:: 210*f1e69953SYanteng Si 211*f1e69953SYanteng Si static void release_entry(struct kref *ref) 212*f1e69953SYanteng Si { 213*f1e69953SYanteng Si /* 所有的工作都是在从kref_put()返回后完成的。*/ 214*f1e69953SYanteng Si } 215*f1e69953SYanteng Si 216*f1e69953SYanteng Si static void put_entry(struct my_data *entry) 217*f1e69953SYanteng Si { 218*f1e69953SYanteng Si mutex_lock(&mutex); 219*f1e69953SYanteng Si if (kref_put(&entry->refcount, release_entry)) { 220*f1e69953SYanteng Si list_del(&entry->link); 221*f1e69953SYanteng Si mutex_unlock(&mutex); 222*f1e69953SYanteng Si kfree(entry); 223*f1e69953SYanteng Si } else 224*f1e69953SYanteng Si mutex_unlock(&mutex); 225*f1e69953SYanteng Si } 226*f1e69953SYanteng Si 227*f1e69953SYanteng Si如果你必须调用其他程序作为释放操作的一部分,而这些程序可能需要很长的时间,或者可 228*f1e69953SYanteng Si能要求相同的锁,那么这真的更有用。请注意,在释放例程中做所有的事情还是比较好的, 229*f1e69953SYanteng Si因为它比较整洁。 230*f1e69953SYanteng Si 231*f1e69953SYanteng Si上面的例子也可以用kref_get_unless_zero()来优化,方法如下:: 232*f1e69953SYanteng Si 233*f1e69953SYanteng Si static struct my_data *get_entry() 234*f1e69953SYanteng Si { 235*f1e69953SYanteng Si struct my_data *entry = NULL; 236*f1e69953SYanteng Si mutex_lock(&mutex); 237*f1e69953SYanteng Si if (!list_empty(&q)) { 238*f1e69953SYanteng Si entry = container_of(q.next, struct my_data, link); 239*f1e69953SYanteng Si if (!kref_get_unless_zero(&entry->refcount)) 240*f1e69953SYanteng Si entry = NULL; 241*f1e69953SYanteng Si } 242*f1e69953SYanteng Si mutex_unlock(&mutex); 243*f1e69953SYanteng Si return entry; 244*f1e69953SYanteng Si } 245*f1e69953SYanteng Si 246*f1e69953SYanteng Si static void release_entry(struct kref *ref) 247*f1e69953SYanteng Si { 248*f1e69953SYanteng Si struct my_data *entry = container_of(ref, struct my_data, refcount); 249*f1e69953SYanteng Si 250*f1e69953SYanteng Si mutex_lock(&mutex); 251*f1e69953SYanteng Si list_del(&entry->link); 252*f1e69953SYanteng Si mutex_unlock(&mutex); 253*f1e69953SYanteng Si kfree(entry); 254*f1e69953SYanteng Si } 255*f1e69953SYanteng Si 256*f1e69953SYanteng Si static void put_entry(struct my_data *entry) 257*f1e69953SYanteng Si { 258*f1e69953SYanteng Si kref_put(&entry->refcount, release_entry); 259*f1e69953SYanteng Si } 260*f1e69953SYanteng Si 261*f1e69953SYanteng Si这对于在put_entry()中移除kref_put()周围的mutex锁是很有用的,但是重要的是 262*f1e69953SYanteng Sikref_get_unless_zero被封装在查找表中的同一关键部分,否则kref_get_unless_zero 263*f1e69953SYanteng Si可能引用已经释放的内存。注意,在不检查其返回值的情况下使用kref_get_unless_zero 264*f1e69953SYanteng Si是非法的。如果你确信(已经有了一个有效的指针)kref_get_unless_zero()会返回true, 265*f1e69953SYanteng Si那么就用kref_get()代替。 266*f1e69953SYanteng Si 267*f1e69953SYanteng SiKrefs和RCU 268*f1e69953SYanteng Si========== 269*f1e69953SYanteng Si 270*f1e69953SYanteng Si函数kref_get_unless_zero也使得在上述例子中使用rcu锁进行查找成为可能:: 271*f1e69953SYanteng Si 272*f1e69953SYanteng Si struct my_data 273*f1e69953SYanteng Si { 274*f1e69953SYanteng Si struct rcu_head rhead; 275*f1e69953SYanteng Si . 276*f1e69953SYanteng Si struct kref refcount; 277*f1e69953SYanteng Si . 278*f1e69953SYanteng Si . 279*f1e69953SYanteng Si }; 280*f1e69953SYanteng Si 281*f1e69953SYanteng Si static struct my_data *get_entry_rcu() 282*f1e69953SYanteng Si { 283*f1e69953SYanteng Si struct my_data *entry = NULL; 284*f1e69953SYanteng Si rcu_read_lock(); 285*f1e69953SYanteng Si if (!list_empty(&q)) { 286*f1e69953SYanteng Si entry = container_of(q.next, struct my_data, link); 287*f1e69953SYanteng Si if (!kref_get_unless_zero(&entry->refcount)) 288*f1e69953SYanteng Si entry = NULL; 289*f1e69953SYanteng Si } 290*f1e69953SYanteng Si rcu_read_unlock(); 291*f1e69953SYanteng Si return entry; 292*f1e69953SYanteng Si } 293*f1e69953SYanteng Si 294*f1e69953SYanteng Si static void release_entry_rcu(struct kref *ref) 295*f1e69953SYanteng Si { 296*f1e69953SYanteng Si struct my_data *entry = container_of(ref, struct my_data, refcount); 297*f1e69953SYanteng Si 298*f1e69953SYanteng Si mutex_lock(&mutex); 299*f1e69953SYanteng Si list_del_rcu(&entry->link); 300*f1e69953SYanteng Si mutex_unlock(&mutex); 301*f1e69953SYanteng Si kfree_rcu(entry, rhead); 302*f1e69953SYanteng Si } 303*f1e69953SYanteng Si 304*f1e69953SYanteng Si static void put_entry(struct my_data *entry) 305*f1e69953SYanteng Si { 306*f1e69953SYanteng Si kref_put(&entry->refcount, release_entry_rcu); 307*f1e69953SYanteng Si } 308*f1e69953SYanteng Si 309*f1e69953SYanteng Si但要注意的是,在调用release_entry_rcu后,结构kref成员需要在有效内存中保留一个rcu 310*f1e69953SYanteng Si宽限期。这可以通过使用上面的kfree_rcu(entry, rhead)来实现,或者在使用kfree之前 311*f1e69953SYanteng Si调用synchronize_rcu(),但注意synchronize_rcu()可能会睡眠相当长的时间。 312