Linux内核里的智能指针

标签: linux 内核 智能指针 | 发表时间:2010-12-02 14:22 | 作者:wwang 浪客
出处:http://www.cnblogs.com/

众所周知,C/C++语言本身并不支持垃圾回收机制,虽然语言本身具有极高的灵活性,但是当遇到大型的项目时,繁琐的内存管理往往让人痛苦异常。现代的C/C++类库一般会提供智能指针来作为内存管理的折中方案,比如STL的auto_ptr,Boost的Smart_ptr库,QT的QPointer家族,甚至是基于C语言构建的GTK+也通过引用计数来实现类似的功能。Linux内核是如何解决这个问题呢?同样作为C语言的解决方案,Linux内核采用的也是引用计数的方式。如果您更熟悉C++,可以把它类比为Boost的shared_ptr,或者是QT的QSharedPointer。

在Linux内核里,引用计数是通过struct kref结构来实现的。在介绍如何使用kref之前,我们先来假设一个情景。假如您开发的是一个字符设备驱动,当设备插上时,系统自动建立一个设备节点,用户通过文件操作来访问设备节点。

如上图所示,最左边的绿色框图表示实际设备的插拔动作,中间黄色的框图表示内核中设备对象的生存周期,右边蓝色的框图表示用户程序系统调用的顺序。如果用户程序正在访问的时候设备突然被拔掉,驱动程序里的设备对象是否立刻释放呢?如果立刻释放,用户程序执行的系统调用一定会发生内存非法访问;如果要等到用户程序close之后再释放设备对象,我们应该怎么来实现?kref就是为了解决类似的问题而生的。

kref的定义非常简单,其结构体里只有一个原子变量。

struct kref {
	atomic_t refcount;
};

Linux内核定义了下面三个函数接口来使用kref:

void kref_init(struct kref *kref);
void kref_get(struct kref *kref);
int kref_put(struct kref *kref, void (*release) (struct kref *kref));

我们先通过一段伪代码来了解一下如何使用kref。

struct my_obj
{
	int val;
	struct kref refcnt;
};

struct my_obj *obj;

void obj_release(struct kref *ref) 
{
	struct my_obj *obj = container_of(ref, struct my_obj, refcnt);
	kfree(obj);
}

device_probe() 
{
	obj = kmalloc(sizeof(*obj), GFP_KERNEL);
	kref_init(&obj->refcnt);
}

device_disconnect() 
{
	kref_put(&obj->refcnt, obj_release);
}

.open() 
{
	kref_get(&obj->refcnt);
}

.close() 
{
	kref_put(&obj->refcnt, obj_release);
}

在这段代码里,我们定义了obj_release来作为释放设备对象的函数,当引用计数为0时,这个函数会被立刻调用来执行真正的释放动作。我们先在device_probe里把引用计数初始化为1,当用户程序调用open时,引用计数又会被加1,之后如果设备被拔掉,device_disconnect会减掉一个计数,但此时refcnt还不是0,设备对象obj并不会被释放,只有当close被调用之后,obj_release才会执行。

看完伪代码之后,我们再来实战一下。为了节省篇幅,这个实作并没有建立一个字符设备,只是通过模块的加载和卸载过程来对感受一下kref。

#include <linux/kernel.h>
#include <linux/module.h>

struct my_obj {
        int val;
        struct kref refcnt;
};

struct my_obj *obj;

void obj_release(struct kref *ref)
{
        struct my_obj *obj = container_of(ref, struct my_obj, refcnt);
        printk(KERN_INFO "obj_release\n");
        kfree(obj);
}

static int __init kreftest_init(void)
{
        printk(KERN_INFO "kreftest_init\n");
        obj = kmalloc(sizeof(*obj), GFP_KERNEL);
        kref_init(&obj->refcnt);
        return 0;
}

static void __exit kreftest_exit(void)
{
        printk(KERN_INFO "kreftest_exit\n");
        kref_put(&obj->refcnt, obj_release);
        return;
}

module_init(kreftest_init);
module_exit(kreftest_exit);

MODULE_LICENSE("GPL");

通过kbuild编译之后我们得到kref_test.ko,然后我们顺序执行以下命令来挂载和卸载模块。

sudo insmod ./kref_test.ko

sudo rmmod kref_test

此时,系统日志会打印出如下消息:

kreftest_init

kreftest_exit

obj_release

这正是我们预期的结果。

 

有了kref引用计数,即使内核驱动写的再复杂,我们对内存管理也应该有信心了吧。

作者: wwang 发表于 2010-12-02 22:22 原文链接

评论: 8 查看评论 发表评论


最新新闻:
· Google斥资约19亿美元收购纽约办公楼(2010-12-03 12:20)
· 企业社会化媒体营销七宗罪(2010-12-03 12:15)
· Android 2.3预计下周发布 十大惊喜不容错过(2010-12-03 12:07)
· 美博客列举5大原因建议谷歌放弃收购Groupon(2010-12-03 11:55)
· 宣传画也挑战下限 黑莓手机用Android系统(2010-12-03 11:44)

编辑推荐:金山安全卫士代码批评

网站导航:博客园首页  我的园子  新闻  闪存  小组  博问  知识库

相关 [linux 内核 智能指针] 推荐:

Linux内核里的智能指针

- 浪客 - 博客园-首页原创精华区
众所周知,C/C++语言本身并不支持垃圾回收机制,虽然语言本身具有极高的灵活性,但是当遇到大型的项目时,繁琐的内存管理往往让人痛苦异常. 现代的C/C++类库一般会提供智能指针来作为内存管理的折中方案,比如STL的auto_ptr,Boost的Smart_ptr库,QT的QPointer家族,甚至是基于C语言构建的GTK+也通过引用计数来实现类似的功能.

Linux 内核优化

- - CSDN博客系统运维推荐文章
声明:本文档来自互联网整理部份加自已实验部份所得:. TCP 服务器 <---> 客户端通信状态.           ACK--------------->                                          建立连接.                    <---------------未回复.

Linux 内核参数优化(for oracle)

- - CSDN博客数据库推荐文章
    Oracle 不同平台的数据库安装指导为我们部署Oracle提供了一些系统参数设置的建议值,然而建议值是在通用的情况下得出的结论,并非能完全满足不同的需求. 使用不同的操作系统内核参数将使得数据库性能相差甚远. 本文描述了linux下几个主要内核参数的设置,供参考.   共享内存是在系统内核分配的一块缓冲区,多个进程都可以访问该缓冲区.

“Linux”不等同于Linux内核:构建Linux系统的8个软件包

- - 极客范 - GeekFan.net
Linux发行版并不仅仅是Linux内核. 所有的Linux发行版都包含了除内核之外的一些必要的软件包,比如Grub引导程序、Bash shell、GNU shell工具软件、守护进程、X.org图形服务器和桌面环境等等. 所有这些不同的程序均由相互独立的开发小组开发. 这些程序被Linux发行版集成到一起从而形成一套完整的“Linux”操作系统.

Google 向新版 Linux 内核贡献技术

- flypen - 谷奥——探寻谷歌的奥秘
本月初刚刚发布的 Linux 2.6.35 版内核中,包含了 RPS 和 RFS 这两项由 Google 贡献的新技术. RPS 的全称是 Receive Packet Steering,这项技术将流入的数据包分布给所有可用的 CPU 去处理,而 RFS (Recevie Flow Steering) 则负责计算哪个核心最适合处理哪项工作.

Linux Kernel 3.0 内核文件系统EXT4 与 Btrfs测试比较

- Power - cnBeta.COM
EXT4文件系统大家可能都比较熟悉了,现在流行的ubuntu 11.04和Fedora15都是默认采用的EXT4,ext4是Linux的第四代扩展文件系统,是EXT3的后继版本. Btrfs 被称为是下一代 Linux 文件系统. 近年来 ext2/3 遇到越来越多的扩展性问题,在期待 ext4 的同时,人们发现了 btrfs,据说它采用了很多先进的文件系统设计,不仅解决了 ext2/3 的扩展性问题,还让人们看到了下一代文件系统所具有的许多其他特性.

Linux 3.1 内核将支持无线局域网唤醒

- bamerl - Wow! Ubuntu
Linux 操作系统早已支持局域网唤醒 (WOL) ,而下一版本的 Linux 3.1 Kernel 系统内核将会引入无线局域网唤醒,简称 WWOL. 和有线版本类似,无线局域网唤醒可以让系统在进入挂起状态 (ACPI S3) 或者另一种低功耗状态的同时继续保持无线连接处于开启状态,而一旦接到用户的特定操作,或者从AP断开连接,就会将系统唤醒,并恢复到原始状态.

Linux 3.1内核将支持无线局域网唤醒

- longman - cnBeta.COM
Linux操作系统早已支持局域网唤醒(WOL),而下一版本的Linux 3.1 Kernel系统内核将会引入无线局域网唤醒,简称WWOL. 和有线版本类似,无线局域网唤醒可以让系统在进入挂起状态(ACPI S3)或者另一种低功耗状态的同时继续保持无线连接处于开启状态,而一旦接到用户的特定操作,或者从AP断开连接,就会将系统唤醒,并恢复到原始状态.

Linux 3.1 RC5版发布,内核开发暂时转移到GitHub

- Tairan Wang - ITeye资讯频道
Linus Torvalds已经发布了Linux 3.1的第5个候选版本,但是该版本没有出现在Linux Kernel官网中,Torvalds已经将Linux内核的主线版本源码转移到了GitHub中. Linux Kernel官网页面. 这源起上个月发生的黑客事件,上周四,kernel.org发布了一则公告,公告称Linux内核源码在8月份早些时候被黑客入侵了,但在8月28日才发现该情况,不过官方称源码本身没有受到影响.

BIOS不支持导致Linux内核耗电增加

- zhipeng - cnBeta.COM
几个月前,Phoronix报道说,自Linux 2.6.38开始,移动Linux用户发现电力消耗迅速飚升,电池续航时间迅速减少,它声称电源退化问题是PCI接口动态电源管理(ASPM)中的一个改变导致的. 现在,它再次报道称Linux 3.0和3.1都没有解决电源退化问题. 实际上,耗电量增加并不是内核,而是BIOS不支持ASPM导致的.