12.3 设备驱动的分层思想
12.3.1 设备驱动核心层和例化
在分层设计时,Linux内核大量使用了面向对象的设计思想。
在面向对象的程序设计中,可以为某一类相似的事物定义一个基类,而具体的事物可以继承这个基类中的函数。对于继承的这个事物,某成员函数的实现与基类一致,那它就可以直接继承基类的函数;相反,它也可以重写(Overriding),对父类的函数进行重新定义。若子类中的方法与父类中的某方法具有相同的方法名、返回类型和参数列表,则新方法将覆盖原有的方法。这种面向对象的“多态”设计思想,提高了代码的可重用能力,是对现实世界中事物之间关系的一种良好呈现。
Linux内核完全是由C语言和汇编语言写成,但是却频繁地用到了面向对象的设计思想。在设备驱动方面,往往为同类的设备设计了一个框架,而框架中的核心层则实现了该设备通用的一些功能。如果具体的设备不想使用核心层的函数,也可以重写。
例如:
return_type core_funca(xxx_device * bottom_dev, param1_type param1, param1_type param2)
{
if (bottom_dev->funca)return bottom_dev->funca(param1, param2);
/* 核心层通用的 funca 代码 */
...
}
在上述core_funca的实现中,会检查底层设备是否重写了funca(),如果重写了,就调用底层的代码,否则,直接使用通用层的。这样做的好处是,核心层的代码可以处理大多数与该类设备的funca()对应的功能,只有少数特殊设备需要重新实现funca()。
再看一个例子:
return_type core_funca(xxx_device * bottom_dev, param1_type param1, param1_type param2)
{
/* 通用的步骤代码 A */
typea_dev_commonA();
...
/* 底层操作 ops1 */
bottom_dev->funca_ops1();
/* 通用的步骤代码 B */
typea_dev_commonB();
...
/* 底层操作 ops2 */
bottom_dev->funca_ops2();
/* 通用的步骤代码 C */
typea_dev_commonB();
...
/** 底层操作 ops3*/
bottom_dev->funca_ops3();
}
上述代码假定为了实现funca(),对于同类设备而言,操作流程一致,都要经过“通用代码A、底层ops1、通用代码B、底层ops2、通用代码C、底层ops3”这几步,分层设计带来的明显好处是,对于通用代码A、B、C,具体的底层驱动不需要再实现,只要关心其底层的操作ops1、ops2、ops3则可。
图12.5反映了设备驱动的核心层与具体设备驱动的关系,这种分层可能只有两层(见图12.5a),也可能是多层的(图12.5b)。