在上一篇文章《Linux内核里的智能指针》里介绍了Linux内核如何使用引用计数来更加安全的管理内存,本文承接前篇,主要介绍几点使用kref时的注意事项。

Linux内核文档kref.txt罗列了三条规则,我们在使用kref时必须遵守。

规则一:

If you make a non-temporary copy of a pointer, especially if  it can be passed to another thread of execution, you must  increment the refcount with kref_get() before passing it off;

规则二:

When you are done with a pointer, you must call kref_put();

规则三:

If the code attempts to gain a reference to a kref-ed structure without already holding a valid pointer, it must serialize access where a kref_put() cannot occur during the kref_get(), and the   structure must remain valid during the kref_get().

对于规则一,其实主要是针对多条执行路径(比如另起一个线程)的情况。如果是在单一的执行路径里,比如把指针传递给一个函数,是不需要使用kref_get的。看下面这个例子:

kref_init(&obj->ref);// do something here
// ...kref_get(&obj->ref);
call_something(obj);
kref_put(&obj->ref);// do something here
// ...kref_put(&obj->ref);

您是不是觉得call_something前后的一对kref_get和kref_put很多余呢?obj并没有逃出我们的掌控,所以它们确实是没有必要的。

但是当遇到多条执行路径的情况就完全不一样了,我们必须遵守规则一。下面是摘自内核文档里的一个例子:

struct my_data
{..struct kref refcount;..
};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;goto out;}.. do stuff with data here.out:kref_put(&data->refcount, data_release);return rv;
}

因为我们并不知道线程more_data_handling何时结束,所以要用kref_get来保护我们的数据。

注意规则一里的那个单词“before",kref_get必须是在传递指针之前进行,在本例里就是在调用kthread_run之前就要执行kref_get,否则,何谈保护呢?

对于规则二我们就不必多说了,前面调用了kref_get,自然要配对使用kref_put。

规则三主要是处理遇到链表的情况。我们假设一个情景,如果有一个链表摆在你的面前,链表里的节点是用引用计数保护的,那你如何操作呢?首先我们需要获得节点的指针,然后才可能调用kref_get来增加该节点的引用计数。根据规则三,这种情况下我们要对上述的两个动作串行化处理,一般我们可以用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_q_entry, 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);
}

这个例子里已经用mutex来进行保护了,假如我们把mutex拿掉,会出现什么情况?记住,我们遇到的很可能是多线程操作。如果线程A在用container_of取得entry指针之后、调用kref_get之前,被线程B抢先执行,而线程B碰巧又做的是kref_put的操作,当线程A恢复执行时一定会出现内存访问的错误,所以,遇到这种情况一定要串行化处理。

我们在使用kref的时候要严格遵循这三条规则,才能安全有效的管理数据。

转载于:https://www.cnblogs.com/wwang/archive/2010/12/03/1895852.html

Linux内核里的“智能指针” (续)相关推荐

  1. Linux 内核里的“智能指针”

    Linux 内核里的"智能指针" from: http://begeek.cn/post/7889.html?ref=myread 众所周知,C/C++语言本身并不支持垃圾回收机制 ...

  2. Linux 内核里的“智能指针”【转】

    转自:http://blog.jobbole.com/88279/ 众所周知,C/C++语言本身并不支持垃圾回收机制,虽然语言本身具有极高的灵活性,但是当遇到大型的项目时,繁琐的内存管理往往让人痛苦异 ...

  3. Linux 内核里的数据结构——基数树

    Linux 内核里的数据结构--基数树 正如你所知道的,Linux内核提供了许多不同的库和函数,它们实现了不同的数据结构和算法.在这部分,我们将研究其中一种数据结构--基数树Radix tree.在 ...

  4. exfat单元分配要设置多少_微软宣布,支持往Linux内核里添加exFAT存储了!跨系统存储的福音...

    栗子 发自 凹非寺  量子位 报道 | 公众号 QbitAI exFAT,是微软开发的文件系统,为闪存而生. 你的U盘.SD卡.手机等等存储设备,能存4G以上的大文件,很大程度上是它的功劳. 可exF ...

  5. linux红黑树节点没有数据,真正理解红黑树,真正的(Linux内核里大量用到的数据 -电脑资料...

    作为一种数据结构,红黑树可谓不算朴素,因为各种宣传让它过于神秘,网上搜罗了一大堆的关于红黑树的文章,不外乎千篇一律,介绍概念,分析性能,贴上代码,然后给上罪恶的一句话,它最坏情况怎么怎么地... 1. ...

  6. linux5关闭apic服务,阐述Linux内核里面的APIC编程

    微软操作系统的火热,你是在应用Linux操作系统么?如果你是Linux操作系统的老用户. 这里为你讲解的问题会对Linux内核里面的APIC编程有所帮助.Linux内核的名字也是"Linux ...

  7. linux内核 current当前进程指针 task_struct结构介绍

    尽管内核模块不象应用程序一样顺序执行, 内核做的大部分动作是代表一个特定进程的. 内核代码可以引用当前进程, 通过存取全局项 current, 它在 <asm/current.h> 中定义 ...

  8. 38 全志平台的script.bin在linux内核里的应用分析

    在全志平台里,script.bin用于指定SOC控制器和GPIO等相关的配置. script.bin是由script.fex用工具转换得来. 在uboot启动系统时, uboot的环境变量bootcm ...

  9. Linux内核中段伪例,利用Linux内核里的Use-After-Free(UAF)漏洞提权

    *本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担. * 作者:nickchang 上个月爆出的CVE-2016-0728 (http: ...

最新文章

  1. Yann LeCun力挺观点:算法对AI提升不大,奇点仍然很遥远
  2. JavaScript实现阶乘递归factorialRecursive算法(附完整源码)
  3. [css] 说说你对table-layout的理解,它有什么运用场景?
  4. 如何查看jar包的版本号?
  5. 训练史上最佳GAN用了512块TPU,一作自述:这不是算法进步,是算力进步
  6. linux用户limit修改,linux – 使用cgroups作为用户设置用户创建的systemd范围的MemoryLimit...
  7. c语言中char buffer,C语言对char*的封装,形成buffer
  8. 图论算法——加权有向图的数据结构
  9. Bloom Filter布隆过滤器
  10. 怎样借助营销圈帮助企业扩大品牌知名度呢?
  11. 【Graph Embedding】node2vec:算法原理,实现和应用
  12. Docker容器访问外部世界
  13. 中国存储器“3+1”版图初现
  14. IBM3650系列服务器前故障诊断面板灯显示含义
  15. 八、正则表达式与JSON
  16. NOI模拟(5.23) TJOID2T3 教科书般的亵渎 (bzoj5339)
  17. Array王锐大神力作:osg与PhysX结合系列内容——第2节 刚体物理表现
  18. 谷歌浏览器开发者工具 preserve log
  19. python 12306登录_Python 实现12306登录功能实例代码
  20. java define 宏_宏定义宏函数(日志输出)define

热门文章

  1. MVC下实现LayUI分页的Demo
  2. 画图讲解SQL join 语句
  3. 虚拟主机 php .htacess,LiteSpeed添加虚拟主机+支持htaccess图文教程
  4. rdf mysql持久化l_Jena 利用数据库保存,持久化本体
  5. oracle中的输入 amp,Oracle之SQL学习
  6. python (第八章)补充-可迭代对象(补充高阶函数,以及常用的高阶函数)
  7. java 中 if与while的区别
  8. 高安全性同态加密算法_坏的同态性教程
  9. 18个项目必备的JavaScript代码片段——数组篇
  10. 第 8 章 容器网络 - 061 - flannel 的连通与隔离