Linux内核大讲堂系列(5)

今天又升级了最新版内核2.6.38.5,编了一下,我之前写的几个模块都可以用,看来这一块没有太大差异。顺便把内核升级写了一个相关的文档。大家如果没升级过内核的可以参考一下:。好了。上节我们用最直观的方式知道了kobject_create_and_add("wwhs_drvmode",NULL)这个函数做了些什么事。那kset呢?经过我们在最前面两小节的分析可以知道kset_register其实就比kobject多了一个函数kobject_uevent。先给出kset_register的定义吧:

int kset_register(struct kset *k)

{

int err;

if (!k)

return -EINVAL;

kset_init(k);

err = kobject_add_internal(&k->kobj);

if (err)

return err;

kobject_uevent(&k->kobj, KOBJ_ADD);

return 0;

}

记起来了吧。这个讲还是为时过早,反正大家只要知道这个东西是用来通知用户空间的就行了。

接下来我们来个大家感兴趣一点的:bus_register。下面这个是经过我改装过的小例子。总共三个文件:

1.wwhs_public.h

#include <linux/module.h>

#include <linux/device.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/stat.h>

#include <linux/slab.h>

#include <linux/kobject.h>

#define wwhs_dbg(dbgbuf) printk(KERN_ERR"wwhs:%s\n",dbgbuf);


2.wwhs_bus.c

#include "../wwhs_public.h"

static int wwhs_bus_match(struct device *dev, struct device_driver *drv)

{

return 1;

}

struct bus_type wwhs_bus_type = {

.name                = "wwhs_bus",

.match               = wwhs_bus_match,

};

static int __init wwhs_bus_init()

{

wwhs_dbg("bus register");

return bus_register(&wwhs_bus_type);

}

static void __exit wwhs_bus_exit()

{

wwhs_dbg("bus unregister");

bus_unregister(&wwhs_bus_type);

}

module_init(wwhs_bus_init);

module_exit(wwhs_bus_exit);

MODULE_AUTHOR("wwhs");

MODULE_DESCRIPTION("wwhs_bus test");

MODULE_LICENSE("GPL");

3.Makefile

obj-m+=wwhs_bus.o

KERNELDIR=/opt/kernel/linux-2.6.38/linux-2.6.38.5

PWD:=$(shell pwd)

all:

make -C $(KERNELDIR) M=$(PWD) modules

clean:

rm -rf *.o* *.ko* *.mod.c *.cmd *.symvers .tmp_versions .*.cmd

编译并安装模块,我们发现在/sys/bus下面多了一个目录:wwhs_bus。进去看看:

Ls

devices  drivers  drivers_autoprobe  drivers_probe  uevent

多了五个东西。

下面我们来结合源码对bug_register进行详细的分析。

int bus_register(struct bus_type *bus)

{

int retval;

struct subsys_private *priv;

priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);

if (!priv)

return -ENOMEM;

priv->bus = bus;

bus->p = priv;

BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);

if (retval)

goto out;

priv->subsys.kobj.kset = bus_kset;

priv->subsys.kobj.ktype = &bus_ktype;

priv->drivers_autoprobe = 1;

retval = kset_register(&priv->subsys);

if (retval)

goto out;

retval = bus_create_file(bus, &bus_attr_uevent);

if (retval)

goto bus_uevent_fail;

priv->devices_kset = kset_create_and_add("devices", NULL,

&priv->subsys.kobj);

if (!priv->devices_kset) {

retval = -ENOMEM;

goto bus_devices_fail;

}

priv->drivers_kset = kset_create_and_add("drivers", NULL,

&priv->subsys.kobj);

if (!priv->drivers_kset) {

retval = -ENOMEM;

goto bus_drivers_fail;

}

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

klist_init(&priv->klist_drivers, NULL, NULL);

retval = add_probe_files(bus);

if (retval)

goto bus_probe_files_fail;

retval = bus_add_attrs(bus);

if (retval)

goto bus_attrs_fail;

pr_debug("bus: '%s': registered\n", bus->name);

return 0;

bus_attrs_fail:

remove_probe_files(bus);

bus_probe_files_fail:

kset_unregister(bus->p->drivers_kset);

bus_drivers_fail:

kset_unregister(bus->p->devices_kset);

bus_devices_fail:

bus_remove_file(bus, &bus_attr_uevent);

bus_uevent_fail:

kset_unregister(&bus->p->subsys);

out:

kfree(bus->p);

bus->p = NULL;

return retval;

}

在函数中首先们感兴趣的是:

priv->subsys.kobj.kset = bus_kset;

priv->subsys.kobj.ktype = &bus_ktype;

priv->drivers_autoprobe = 1;

我们通过对代码进行查找发现bus_kset其定义中下:

static struct kset *bus_kset;

很明显会有别的地方对它已经进行初始化了。我们在bus.c文件中发现有这样的一个函数:

int __init buses_init(void)

{

bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);

if (!bus_kset)

return -ENOMEM;

return 0;

}

而bus_uevent_ip的定义如下:

static const struct kset_uevent_ops bus_uevent_ops = {

.filter = bus_uevent_filter,

};

其中��数bus_uevent_filter,定义如下:

static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)

{

struct kobj_type *ktype = get_ktype(kobj);

if (ktype == &bus_ktype)

return 1;

return 0;

}

OK,下一个, bus_ktype:

static struct kobj_type bus_ktype = {

.sysfs_ops        = &bus_sysfs_ops,

};

static const struct sysfs_ops bus_sysfs_ops = {

.show       = bus_attr_show,

.store       = bus_attr_store,

};

static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,

char *buf)

{

struct bus_attribute *bus_attr = to_bus_attr(attr);

struct subsys_private *subsys_priv = to_subsys_private(kobj);

ssize_t ret = 0;

if (bus_attr->show)

ret = bus_attr->show(subsys_priv->bus, buf);

return ret;

}

static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,

const char *buf, size_t count)

{

struct bus_attribute *bus_attr = to_bus_attr(attr);

struct subsys_private *subsys_priv = to_subsys_private(kobj);

ssize_t ret = 0;

if (bus_attr->store)

ret = bus_attr->store(subsys_priv->bus, buf, count);

return ret;

}

接下来是:

kset_register(&priv->subsys);

又见到老朋友了,这个老朋友将会帮我们在/sys/bus下创建一个名为wwhs_bus的目录。

接下来是bus_create_file(bus, &bus_attr_uevent);

bus_attr_uevent 是什么呢?用sourceinsight看是黑的,直接点击的话是跳转不了的,另外有很多高手推荐的vim、emacs,或者再装上高级的插件cscope、ctags也肯定是直接找不到的。

除非你自已根据linux内核的语法规则自已再写个插件还差不多,但前提条件是你要先手工找到熟悉规则才能写得出来,否则,你永远也别想直接找到。

不过没事,哥已经帮你找到了,在这:

static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);

再看gh BUS_ATTR这个宏:

#define BUS_ATTR(_name, _mode, _show, _store)        \

struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)

明白了吧。

static ssize_t bus_uevent_store(struct bus_type *bus,

const char *buf, size_t count)

{

enum kobject_action action;

if (kobject_action_type(buf, count, &action) == 0)

kobject_uevent(&bus->p->subsys.kobj, action);

return count;

}

很明显我们上面分析的这一堆代码会在/sys/bus/wwhs_bus目录之下创建一个叫uevent的文件。并且我们如果往这个文件中写入字符的时候会触发bus_uevent_store函数的执行。这个函数实在简单的连我这种笨鸟都不想分析,各位大侠就自个看一看吧。

接下来要登场的是一女一男,女的不但身材绝美,面容娇好,身怀绝技,且用情专一,一生只为一个人服务。男的虽然才高八斗,学富五车,但是确是个花心大萝卜,只要能达到他的条件,都会为女方服务,比如我们马上要讲的这位男同志,就只要身材好,漂亮,他就愿意为人家服务。扯了半天,我们的美女就是device,帅哥就是device_driver。总线作为红娘,肯定同一时间不止为一对男女提供机会,<<非诚勿扰>>和<<我们约会吧>>这种市井节目都知道一次邀请多位男女同志一起配对,难道我们英明神武的内核开发者不会?不可能嘛。所以我们内核作者和何炅老师一样的聪明,他分别利用下面这段代码创建了一个美女阵营和一个帅哥阵营。

priv->devices_kset = kset_create_and_add("devices", NULL,

&priv->subsys.kobj);         //美女阵营

if (!priv->devices_kset) {

retval = -ENOMEM;

goto bus_devices_fail;

}

priv->drivers_kset = kset_create_and_add("drivers", NULL,

&priv->subsys.kobj);      //帅哥阵营

if (!priv->drivers_kset) {

retval = -ENOMEM;

goto bus_drivers_fail;

}

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  //美女阵营

klist_init(&priv->klist_drivers, NULL, NULL);                                                 //帅哥阵营

接下来就是:

static int add_probe_files(struct bus_type *bus)

{

int retval;

retval = bus_create_file(bus, &bus_attr_drivers_probe);

if (retval)

goto out;

retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);

if (retval)

bus_remove_file(bus, &bus_attr_drivers_probe);

out:

return retval;

}

凭着刚才对bus_attr_uevent 的分析,我们可以利用人本身的惰性来查找上面的几个在sourceinsight下的灰色成员。

是的这两个成员就是:

static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);

static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,

show_drivers_autoprobe, store_drivers_autoprobe);

在这里可以告诉大家一个技巧:linux内核在用宏定义这类变量时,都会在需要调用这个变量的最近的地方定义,不会离的很远。

显然,我们会在/sys/bus/wwhs_bus/目录下再创建两个文件:drivers_autoprobe 、drivers_probe。都是很简单的几个函数。列一下吧:

static ssize_t store_drivers_probe(struct bus_type *bus,

const char *buf, size_t count)

{

struct device *dev;

dev = bus_find_device_by_name(bus, NULL, buf);

if (!dev)

return -ENODEV;

if (bus_rescan_devices_helper(dev, NULL) != 0)

return -EINVAL;

return count;

}

static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)

{

return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);

}

static ssize_t store_drivers_autoprobe(struct bus_type *bus,

const char *buf, size_t count)

{

if (buf[0] == '0')

bus->p->drivers_autoprobe = 0;

else

bus->p->drivers_autoprobe = 1;

return count;

}

至此bug_register分析完了,六个文件(目录也是文件)也创建完成了。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/24202.html