最近看驱动开发,看linux的device model,有一些感触。暂记于下。
我之前一直把OO(object oriented)这个概念和特定的语言联系在一起。比如,认为c++,java,python等是面向对象的语言,C语言就不是。这种想法不太对,而且有时很危险,至少,这样的想法会限制你解决问题的思路。
OO和语言应该分开来看。OO是一种解决问题的概念,是对问题的一种抽象;而编程语言,是解决问题的工具。
以下线简单谈谈kobject和container_of.
从oo的观点来看,kobject就是一个super class, 配合container_of这个宏,实现了运行时的动态绑定特性。
在kernel中,几乎所有的device,都会有一个kobject域,比如cdev
struct cdev {
struct kobject kobj;
struct module *owner;
struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
这个kobject是用来提供一些基础服务的,如reference counting, data structure glue, hotplug handing等。我们可以把这些structure都看成是kobject的派生类。那么问题就来了,这些派生类会有很多种,而他们的好多操作,从含义上来讲,是一样的,比如create,比如remove等。我们自然可以为每一个类定义它对应的操作,这对实现没有问题。但是,上层调用这些类的时候,面对每一个含义相似的操作却有不同的参数类型,会是何等蛋疼的一件事情!!!
在c++中,我们是通过传递基类指针或者引用的方式,通过动态绑定来解决这个问题的。
但是,c语言没有内建这种面向对象的机制。它是如何来解决这个问题的呢?
今天,简单只谈一点:container_of
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
这个宏的作用是,通过结构的一个域,来取得该结构的指针。
比如有某个kobject* pk指向某个kobject。而此kobject是cdev中一员。可以用以下语句,取得该cdev结构的指针
container_of(pk, cdev, kobj);
因为所有device structure都包含kobject域,所以,我们可以给一些语义想通的操作,传递kobject指针,而在函数内部,利用container_of来取得实际的对象的指针,从而进行相应操作。
以下,简单解析下container_of宏。
其结构是({A;B;})。主要原理是通过指针操作(加减一定的数值)以及强制类型转换,来返回指向某个内存的一个特定类型的指针。原理比较简单,有两点可能会有些疑惑,如下。
1.({A;B;})的含义。 对c和c++熟悉的人应该知道,它返回的值是B表达式的值。怀疑的可以试一下printf("%d", ({1;2;3;});
2.typeof的含义。typeof是c新引进的关键词。参考如下连接。typeof的实现方法想来比较简单,就是在稍微改下编译器的实现就ok了。在编译器关键词中天剑typeof,然后进行语法处理的时候对typeof的表达式进行分析求值。
http://gcc.gnu.org/onlinedocs/gcc/Typeof.html