.. include:: ../disclaimer-zh_CN.rst :Original: Documentation/core-api/kref.rst 翻译: å¸å»¶è…¾ Yanteng Si <siyanteng@loongson.cn> æ ¡è¯‘ï¼š <æ¤å¤„è¯·æ ¡è¯‘å‘˜ç¾åï¼ˆè‡ªæ„¿ï¼‰ï¼Œæˆ‘å°†åœ¨ä¸‹ä¸€ä¸ªç‰ˆæœ¬æ·»åŠ > .. _cn_core_api_kref.rst: ================================= ä¸ºå†…æ ¸å¯¹è±¡æ·»åŠ å¼•ç”¨è®¡æ•°å™¨ï¼ˆkrefs) ================================= :作者: Corey Minyard <minyard@acm.org> :作者: Thomas Hellstrom <thellstrom@vmware.com> å…¶ä¸å¾ˆå¤šå†…容都是从Greg Kroah-Hartman2004年关于krefsçš„OLS论文和演讲ä¸æ‘˜ 录的,å¯ä»¥åœ¨ä»¥ä¸‹ç½‘å€æ‰¾åˆ°: - http://www.kroah.com/linux/talks/ols_2004_kref_paper/Reprint-Kroah-Hartman-OLS2004.pdf - http://www.kroah.com/linux/talks/ols_2004_kref_talk/ 简介 ==== krefså…è®¸ä½ ä¸ºä½ çš„å¯¹è±¡æ·»åŠ å¼•ç”¨è®¡æ•°å™¨ã€‚å¦‚æžœä½ æœ‰åœ¨å¤šä¸ªåœ°æ–¹ä½¿ç”¨å’Œä¼ é€’çš„å¯¹è±¡ï¼Œ è€Œä½ æ²¡æœ‰refcountsï¼Œä½ çš„ä»£ç å‡ ä¹Žè‚¯å®šæ˜¯åçš„ã€‚å¦‚æžœä½ æƒ³è¦å¼•ç”¨è®¡æ•°ï¼Œkrefs是个 好办法。 è¦ä½¿ç”¨krefï¼Œè¯·åœ¨ä½ çš„æ•°æ®ç»“æž„ä¸æ·»åŠ 一个,如:: struct my_data { . . struct kref refcount; . . }; krefå¯ä»¥å‡ºçŽ°åœ¨æ•°æ®ç»“构体ä¸çš„任何地方。 åˆå§‹åŒ– ====== ä½ å¿…é¡»åœ¨åˆ†é…kref之åŽåˆå§‹åŒ–它。 è¦åšåˆ°è¿™ä¸€ç‚¹ï¼Œå¯ä»¥è¿™æ ·è°ƒç”¨kref_init:: struct my_data *data; data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; kref_init(&data->refcount); 这将krefä¸çš„refcount设置为1。 Kref规则 ======== ä¸€æ—¦ä½ æœ‰ä¸€ä¸ªåˆå§‹åŒ–çš„krefï¼Œä½ å¿…é¡»éµå¾ªä»¥ä¸‹è§„则: 1) å¦‚æžœä½ å¯¹ä¸€ä¸ªæŒ‡é’ˆåšäº†ä¸€ä¸ªéžä¸´æ—¶æ€§çš„æ‹·è´ï¼Œç‰¹åˆ«æ˜¯å¦‚果它å¯ä»¥è¢«ä¼ 递给å¦ä¸€ä¸ªæ‰§ è¡Œçº¿ç¨‹ï¼Œä½ å¿…é¡»åœ¨ä¼ é€’ä¹‹å‰ç”¨kref_get()å¢žåŠ refcount:: kref_get(&data->refcount); å¦‚æžœä½ å·²ç»æœ‰äº†ä¸€ä¸ªæŒ‡å‘kref-ed结构体的有效指针(refcountä¸èƒ½ä¸ºé›¶ï¼‰ï¼Œä½ å¯ä»¥åœ¨æ²¡æœ‰é”çš„æƒ…å†µä¸‹è¿™æ ·åšã€‚ 2) å½“ä½ å®Œæˆå¯¹ä¸€ä¸ªæŒ‡é’ˆçš„处ç†æ—¶ï¼Œä½ 必须调用kref_put():: kref_put(&data->refcount, data_release); 如果这是对该指针的最åŽä¸€æ¬¡å¼•ç”¨ï¼Œé‡Šæ”¾ç¨‹åºå°†è¢«è°ƒç”¨ã€‚如果代ç 从æ¥æ²¡æœ‰å°è¯•è¿‡ 在没有已ç»æŒæœ‰æœ‰æ•ˆæŒ‡é’ˆçš„情况下获得一个kref-ed结构体的有效指针,那么在没 有é”çš„æƒ…å†µä¸‹è¿™æ ·åšæ˜¯å®‰å…¨çš„。 3) 如果代ç 试图获得对一个kref-ed结构体的引用,而ä¸æŒæœ‰ä¸€ä¸ªæœ‰æ•ˆçš„指针,它必 须按顺åºè®¿é—®ï¼Œåœ¨kref_put()期间ä¸èƒ½å‘生kref_get(),并且该结构体在kref_get() 期间必须ä¿æŒæœ‰æ•ˆã€‚ ä¾‹å¦‚ï¼Œå¦‚æžœä½ åˆ†é…了一些数æ®ï¼Œç„¶åŽå°†å…¶ä¼ 递给å¦ä¸€ä¸ªçº¿ç¨‹æ¥å¤„ç†:: void data_release(struct kref *ref) { struct my_data *data = container_of(ref, struct my_data, refcount); kfree(data); } void more_data_handling(void *cb_data) { struct my_data *data = cb_data; . . do stuff with data here . kref_put(&data->refcount, data_release); } int my_data_handler(void) { int rv = 0; struct my_data *data; struct task_struct *task; data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; kref_init(&data->refcount); kref_get(&data->refcount); task = kthread_run(more_data_handling, data, "more_data_handling"); if (task == ERR_PTR(-ENOMEM)) { rv = -ENOMEM; kref_put(&data->refcount, data_release); goto out; } . . do stuff with data here . out: kref_put(&data->refcount, data_release); return rv; } è¿™æ ·ï¼Œä¸¤ä¸ªçº¿ç¨‹å¤„ç†æ•°æ®çš„顺åºå¹¶ä¸é‡è¦ï¼Œkref_put()处ç†çŸ¥é“æ•°æ®ä¸å†è¢«å¼•ç”¨å¹¶é‡Š 放它。kref_get()ä¸éœ€è¦é”ï¼Œå› ä¸ºæˆ‘ä»¬å·²ç»æœ‰äº†ä¸€ä¸ªæœ‰æ•ˆçš„指针,我们拥有一个 refcount。putä¸éœ€è¦é”ï¼Œå› ä¸ºæ²¡æœ‰ä»»ä½•ä¸œè¥¿è¯•å›¾åœ¨æ²¡æœ‰æŒæœ‰æŒ‡é’ˆçš„情况下获å–æ•°æ®ã€‚ 在上é¢çš„例åä¸ï¼Œkref_put()在æˆåŠŸå’Œé”™è¯¯è·¯å¾„ä¸éƒ½ä¼šè¢«è°ƒç”¨2次。这是必è¦çš„ï¼Œå› ä¸ºå¼•ç”¨è®¡æ•°è¢«kref_init()å’Œkref_get()递增了2次。 请注æ„,规则1ä¸çš„ "before "是éžå¸¸é‡è¦çš„ã€‚ä½ ä¸åº”该åšç±»ä¼¼äºŽ:: task = kthread_run(more_data_handling, data, "more_data_handling"); if (task == ERR_PTR(-ENOMEM)) { rv = -ENOMEM; goto out; } else /* BAD BAD BAD - 在交接åŽå¾—到 */ kref_get(&data->refcount); ä¸è¦ä»¥ä¸ºä½ 知é“自己在åšä»€ä¹ˆè€Œä½¿ç”¨ä¸Šè¿°æž„é€ ã€‚é¦–å…ˆï¼Œä½ å¯èƒ½ä¸çŸ¥é“自己在åšä»€ä¹ˆã€‚ å…¶æ¬¡ï¼Œä½ å¯èƒ½çŸ¥é“自己在åšä»€ä¹ˆï¼ˆæœ‰äº›æƒ…况下涉åŠåˆ°é”,上述åšæ³•å¯èƒ½æ˜¯åˆæ³•çš„), 但其他ä¸çŸ¥é“自己在åšä»€ä¹ˆçš„人å¯èƒ½ä¼šæ”¹å˜ä»£ç 或å¤åˆ¶ä»£ç 。这是很å±é™©çš„作风。请 ä¸è¦è¿™æ ·åšã€‚ åœ¨æœ‰äº›æƒ…å†µä¸‹ï¼Œä½ å¯ä»¥ä¼˜åŒ–getå’Œputã€‚ä¾‹å¦‚ï¼Œå¦‚æžœä½ å·²ç»å®Œæˆäº†ä¸€ä¸ªå¯¹è±¡ï¼Œå¹¶ä¸”给其 ä»–å¯¹è±¡æŽ’é˜Ÿï¼Œæˆ–è€…æŠŠå®ƒä¼ é€’ç»™å…¶ä»–å¯¹è±¡ï¼Œé‚£ä¹ˆå°±æ²¡æœ‰ç†ç”±å…ˆåšä¸€ä¸ªget,然åŽå†åšä¸€ä¸ª put:: /* 糟糕的é¢å¤–获å–(get)和输出(put) */ kref_get(&obj->ref); enqueue(obj); kref_put(&obj->ref, obj_cleanup); åªè¦åšenqueueå°±å¯ä»¥äº†ã€‚ 我们éšæ—¶æ¬¢è¿Žå¯¹è¿™ä¸ªé—®é¢˜çš„评论:: enqueue(obj); /* 我们已ç»å®Œæˆäº†å¯¹obj的处ç†ï¼Œæ‰€ä»¥æˆ‘们把我们的refcountä¼ ç»™äº†é˜Ÿåˆ—ã€‚ 在这之åŽä¸è¦å†ç¢°obj了! */ 最åŽä¸€æ¡è§„则(规则3)是最难处ç†çš„一æ¡ã€‚ä¾‹å¦‚ï¼Œä½ æœ‰ä¸€ä¸ªæ¯ä¸ªé¡¹ç›®éƒ½è¢«krefed的列表, è€Œä½ å¸Œæœ›å¾—åˆ°ç¬¬ä¸€ä¸ªé¡¹ç›®ã€‚ä½ ä¸èƒ½åªæ˜¯ä»Žåˆ—表ä¸æŠ½å‡ºç¬¬ä¸€ä¸ªé¡¹ç›®ï¼Œç„¶åŽkref_get()它。 è¿™è¿å了规则3ï¼Œå› ä¸ºä½ è¿˜æ²¡æœ‰æŒæœ‰ä¸€ä¸ªæœ‰æ•ˆçš„æŒ‡é’ˆã€‚ä½ å¿…é¡»æ·»åŠ ä¸€ä¸ªmutex(或其他é”)。 比如说:: static DEFINE_MUTEX(mutex); static LIST_HEAD(q); struct my_data { struct kref refcount; struct list_head link; }; static struct my_data *get_entry() { struct my_data *entry = NULL; mutex_lock(&mutex); if (!list_empty(&q)) { entry = container_of(q.next, struct my_data, link); kref_get(&entry->refcount); } mutex_unlock(&mutex); return entry; } static void release_entry(struct kref *ref) { struct my_data *entry = container_of(ref, struct my_data, refcount); list_del(&entry->link); kfree(entry); } static void put_entry(struct my_data *entry) { mutex_lock(&mutex); kref_put(&entry->refcount, release_entry); mutex_unlock(&mutex); } å¦‚æžœä½ ä¸æƒ³åœ¨æ•´ä¸ªé‡Šæ”¾æ“作过程ä¸æŒæœ‰é”,kref_put()的返回值是有用的。å‡è®¾ä½ ä¸æƒ³åœ¨ 上é¢çš„例åä¸åœ¨æŒæœ‰é”的情况下调用kfree()ï¼ˆå› ä¸ºè¿™æ ·åšæœ‰ç‚¹æ— æ„ä¹‰ï¼‰ã€‚ä½ å¯ä»¥ä½¿ç”¨kref_put(), 如下所示:: static void release_entry(struct kref *ref) { /* 所有的工作都是在从kref_put()返回åŽå®Œæˆçš„。*/ } static void put_entry(struct my_data *entry) { mutex_lock(&mutex); if (kref_put(&entry->refcount, release_entry)) { list_del(&entry->link); mutex_unlock(&mutex); kfree(entry); } else mutex_unlock(&mutex); } å¦‚æžœä½ å¿…é¡»è°ƒç”¨å…¶ä»–ç¨‹åºä½œä¸ºé‡Šæ”¾æ“作的一部分,而这些程åºå¯èƒ½éœ€è¦å¾ˆé•¿çš„æ—¶é—´ï¼Œæˆ–è€…å¯ èƒ½è¦æ±‚相åŒçš„é”,那么这真的更有用。请注æ„,在释放例程ä¸åšæ‰€æœ‰çš„事情还是比较好的, å› ä¸ºå®ƒæ¯”è¾ƒæ•´æ´ã€‚ 上é¢çš„例å也å¯ä»¥ç”¨kref_get_unless_zero()æ¥ä¼˜åŒ–,方法如下:: static struct my_data *get_entry() { struct my_data *entry = NULL; mutex_lock(&mutex); if (!list_empty(&q)) { entry = container_of(q.next, struct my_data, link); if (!kref_get_unless_zero(&entry->refcount)) entry = NULL; } mutex_unlock(&mutex); return entry; } static void release_entry(struct kref *ref) { struct my_data *entry = container_of(ref, struct my_data, refcount); mutex_lock(&mutex); list_del(&entry->link); mutex_unlock(&mutex); kfree(entry); } static void put_entry(struct my_data *entry) { kref_put(&entry->refcount, release_entry); } 这对于在put_entry()ä¸ç§»é™¤kref_put()周围的mutexé”是很有用的,但是é‡è¦çš„是 kref_get_unless_zero被å°è£…在查找表ä¸çš„åŒä¸€å…³é”®éƒ¨åˆ†ï¼Œå¦åˆ™kref_get_unless_zero å¯èƒ½å¼•ç”¨å·²ç»é‡Šæ”¾çš„内å˜ã€‚注æ„,在ä¸æ£€æŸ¥å…¶è¿”回值的情况下使用kref_get_unless_zero 是éžæ³•çš„ã€‚å¦‚æžœä½ ç¡®ä¿¡ï¼ˆå·²ç»æœ‰äº†ä¸€ä¸ªæœ‰æ•ˆçš„指针)kref_get_unless_zero()会返回true, 那么就用kref_get()代替。 Krefså’ŒRCU ========== 函数kref_get_unless_zero也使得在上述例åä¸ä½¿ç”¨rcué”进行查找æˆä¸ºå¯èƒ½:: struct my_data { struct rcu_head rhead; . struct kref refcount; . . }; static struct my_data *get_entry_rcu() { struct my_data *entry = NULL; rcu_read_lock(); if (!list_empty(&q)) { entry = container_of(q.next, struct my_data, link); if (!kref_get_unless_zero(&entry->refcount)) entry = NULL; } rcu_read_unlock(); return entry; } static void release_entry_rcu(struct kref *ref) { struct my_data *entry = container_of(ref, struct my_data, refcount); mutex_lock(&mutex); list_del_rcu(&entry->link); mutex_unlock(&mutex); kfree_rcu(entry, rhead); } static void put_entry(struct my_data *entry) { kref_put(&entry->refcount, release_entry_rcu); } 但è¦æ³¨æ„的是,在调用release_entry_rcuåŽï¼Œç»“æž„krefæˆå‘˜éœ€è¦åœ¨æœ‰æ•ˆå†…å˜ä¸ä¿ç•™ä¸€ä¸ªrcu 宽é™æœŸã€‚è¿™å¯ä»¥é€šè¿‡ä½¿ç”¨ä¸Šé¢çš„kfree_rcu(entry, rhead)æ¥å®žçŽ°ï¼Œæˆ–者在使用kfreeä¹‹å‰ è°ƒç”¨synchronize_rcu(),但注æ„synchronize_rcu()å¯èƒ½ä¼šç¡çœ 相当长的时间。