《Linux内核设计与实现》学习【12】—— 设备与模块

1 设备类型

类型特点例子
字符设备一个个字符方式访问键盘
块设备以块为单位,对数据随机访问flash
网络设备打破了“一切皆文件”的原则网卡

其他设备类型:
  杂项设备——简化的字符设备
  伪设备——虚拟的,例如/dev/null(空设备), /dev/zero(零设备)

2 模块

(1)构建模块方式
  1)放在内核源码树中
  2)放在内核代码外
  此时,写个Makefile较简单

KVERS = $(shell uname -r)
obj-m += hello.o
build: kernel_modules
EXTRA_CFLAGS=-g -o0
kernel_modules:
	make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
	make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

(2)载入

insmod module.ko
rmmod module

  若模块依赖其他的模块,可使用modprobe命令,会自动加载依赖的模块。
(3)参数
  驱动程序声明的参数,允许应用程序在加载模块时指定参数

module_param(name, type, perm) //定义一个模块参数
module_param_named(name, variable, type, perm) //定义一个模块参数,并且参数对内对外的名称不一样
module_param_string(name, string, len, perm) //拷贝字符串到指定的字符数组
module_param_array(name, type, nump, perm) //定义数组类型的模块参数

(4)导出符号表
  只有被显示导出后的外部函数,才能被动态库调用。

EXPORT_SYMBOL(函数名)       
EXPORT_SYMBOL_GPL(函数名)   

3 设备模型

  内核最初为了电源管理,提出了设备模型来管理所有的设备,建立了系统中设备的拓扑关系。
  sysfs文件系统是一个虚拟文件系统,提供了系统中各种设备的拓扑结构,通过输入“tree /sys”,可以看到拓扑结构

|--block
|--bus
	|--platform
	|--serio
|--class
|--dev
|--devices
|--firmware
...

  最重要的目录是devices目录,这就是系统中实际的设备拓扑。

3.1 结构体

(1)kobject
  核心部分,一个kobject对应sysfs里的一个目录

struct kobject {
    const char        *name;                   /* kobject 名称 */
    struct list_head    entry;               /* kobject 链表 */
    struct kobject        *parent;             /* kobject 的父对象,说明kobject是有层次结构的 */
    struct kset        *kset;                   /* kobject 的集合,接下来有详细介绍 */
    struct kobj_type    *ktype;              /* kobject 的类型,接下来有详细介绍 */
    struct sysfs_dirent    *sd;                 /* 在sysfs中,这个结构体表示kobject的一个inode结构体,sysfs之后也会介绍 */
    struct kref        kref;                    /* 提供 kobject 的引用计数 */
    /* 一些标志位  */
    unsigned int state_initialized:1;       
    unsigned int state_in_sysfs:1;
    unsigned int state_add_uevent_sent:1;
    unsigned int state_remove_uevent_sent:1;
    unsigned int uevent_suppress:1;
};

  kobject 本身不代表什么实际的内容,一般都是嵌在其他数据结构中来发挥作用。例如cdev

struct cdev {
    struct kobject kobj;    /* 嵌在 cdev 中的kobject */
    struct module *owner;   
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

(2)ktype
  ktype是为了描述一族kobject所具有的普遍属性

struct kobj_type {
    void (*release)(struct kobject *kobj);  /* kobject的引用计数降到0时触发的析构函数,负责释放和清理内存的工作 */
    struct sysfs_ops *sysfs_ops;            /* sysfs操作相关的函数 */
    struct attribute **default_attrs;       /* kobject 相关的默认属性 */
};

(3)kset
  kset是kobject对象的集合体,可以所有相关的kobject置于一个kset之中

struct kset {
    struct list_head list;    /* 表示kset中所有kobject的链表 */
    spinlock_t list_lock;     /* 用于保护 list 的自旋锁*/
    struct kobject kobj;      /* kset中嵌入的一个kobject,使得kset也可以表现的像一样kobject一样*/
    struct kset_uevent_ops *uevent_ops;  /* 处理kset中kobject的热插拔事件 提供了与用户空间热插拔进行通信的机制 */
};

(4)kref
  kobject的引用计数减为0时,对象便会释放。

struct kref {
    atomic_t refcount;  /* 只有一个表示引用计数的属性,atomic_t 类型表示对它的访问是原子操作 */
};

  操作

void kref_put(struct kref *kref, int num);  /* 减少引用计数 -1 */
void kref_get(struct kref *kref);           /* 增加引用计数 +1 */

(5)关系
在这里插入图片描述

  kobject与一个特别的ktype关联,在kobject中ktype字段指向该对象。kobject又归入kset集合,kset提供了两个功能:
  1)其中嵌入的kobject作为kobject组的基类
  2)kset将相关的kobjcet集合在一起

3.2 程序实例

  建立的目录

my_kset/
|-- mykobj1
|   |-- name
|   `-- val
`-- mykobj2
    |-- name
    `-- val

  代码

#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/kobject.h>
#include<linux/sysfs.h>

MODULE_LICENSE("Dual BSD/GPL");

static void myobj_release(struct kobject*);
static ssize_t my_show(struct kobject *, struct attribute *, char *);
static ssize_t my_store(struct kobject *, struct attribute *, const char *, size_t);

/* 自定义的结构体,2个属性,并且嵌入了kobject */
struct my_kobj 
{
    int ival;
    char* cname;
    struct kobject kobj;
};

static struct my_kobj *myobj = NULL;

/* my_kobj 的属性 ival 所对应的sysfs中的文件,文件名 val */
static struct attribute val_attr = {
    .name = "val",
    .owner = NULL,
    .mode = 0666,
};

/* my_kobj 的属性 cname 所对应的sysfs中的文件,文件名 name */
static struct attribute name_attr = {
    .name = "name",
    .owner = NULL,
    .mode = 0666,
};

static int test_kobject_default_attr_init(void)
{
    struct attribute *myattrs[] = {NULL, NULL, NULL};
    struct sysfs_ops *myops = NULL;
    struct kobj_type *mytype = NULL;

    /* 初始化 myobj */
    myobj = kmalloc(sizeof(struct my_kobj), GFP_KERNEL);
    if (myobj == NULL)
        return -ENOMEM;

    /* 配置文件 val 的默认值 */
    myobj->ival = 100;
    myobj->cname = "test";

    /* 初始化 ktype */
    mytype = kmalloc(sizeof(struct kobj_type), GFP_KERNEL);
    if (mytype == NULL)
        return -ENOMEM;

    /* 增加2个默认属性文件 */
    myattrs[0] = &val_attr;
    myattrs[1] = &name_attr;

    /* 初始化ktype的默认属性和析构函数 */
    mytype->release = myobj_release;
    mytype->default_attrs = myattrs;

    /* 初始化ktype中的 sysfs */
    myops = kmalloc(sizeof(struct sysfs_ops), GFP_KERNEL);
    if (myops == NULL)
        return -ENOMEM;

    myops->show = my_show;
    myops->store = my_store;
    mytype->sysfs_ops = myops;

    /* 初始化kobject,并加入到sysfs中 */
    memset(&myobj->kobj, 0, sizeof(struct kobject)); /* 这一步非常重要,没有这一步init kobject会失败 */
    if (kobject_init_and_add(&myobj->kobj, mytype, NULL, "test_kobj_default_attr"))
        kobject_put(&myobj->kobj);

    printk(KERN_ALERT "*************************\n");
    printk(KERN_ALERT "test_kobject_default_attr is inited!\n");
    printk(KERN_ALERT "*************************\n");
    
    return 0;
}

static void test_kobject_default_attr_exit(void)
{
    kobject_del(&myobj->kobj);
    kfree(myobj);
    
    /* 退出内核模块 */
    printk(KERN_ALERT "*************************\n");
    printk(KERN_ALERT "test_kobject_default_attr is exited!\n");
    printk(KERN_ALERT "*************************\n");
    printk(KERN_ALERT "\n\n\n\n\n");
}

static void myobj_release(struct kobject *kobj) 
{
    printk(KERN_ALERT, "release kobject");
    kobject_del(kobj);
}

/* 读取属性文件 val 或者name时会执行此函数 */
static ssize_t my_show(struct kobject *kboj, struct attribute *attr, char *buf) 
{
    printk(KERN_ALERT "SHOW -- attr-name: [%s]\n", attr->name);    
    if (strcmp(attr->name, "val") == 0)
        return sprintf(buf, "%d\n", myobj->ival);
    else
        return sprintf(buf, "%s\n", myobj->cname);
}

/* 写入属性文件 val 或者name时会执行此函数 */
static ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) 
{
    printk(KERN_ALERT "STORE -- attr-name: [%s]\n", attr->name);
    if (strcmp(attr->name, "val") == 0)
        sscanf(buf, "%d\n", &myobj->ival);
    else
        sscanf(buf, "%s\n", myobj->cname);        
    return len;
}

module_init(test_kobject_default_attr_init);
module_exit(test_kobject_default_attr_exit);

说明:
(1)添加和删除kobject

int kobject_add(struct kobject *kobj,
                    struct kobject *parent,
                    const char *fmt, ...);   /* 设置此kobject的parent,将此kobject加入到现有对象层次结构中 */
void kobject_del(struct kobject *kobj); /* 将此kobject从现有对象层次结构中取消 */

(2)属性
  默认属性default_attrs

struct attribute {
    const char        *name;   /* sysfs文件树中的文件名 */
    struct module        *owner; /* x86体系结构中已经不再继续使用了,可能在其他体系结构中还会使用 */
    mode_t            mode;   /* sysfs中该文件的权限 */
};

  sys字段描述如何使用它们

struct sysfs_ops {
    /* 在读sysfs文件时该方法被调用 */
    ssize_t    (*show)(struct kobject *kobj, struct attribute *attr,char *buffer);
    /* 在写sysfs文件时该方法被调用 */
    ssize_t    (*store)(struct kobject *kobj,struct attribute *attr,const char *buffer, size_t size);
};

3.3 内核事件

  内核事件层实现了内核到用户的消息通知机制,该机制就是建立在kobject基础上的。
  内核向用户空间发送信号使用函数

int kobject_uevent(struct kobject *kobj, enum kobject_action action);

参考:
Linux设备模型 (1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值