发新话题
打印

linux字符设备驱动分析笔记(二)

linux字符设备驱动分析笔记(二)

三.初始化和添加字符设备
在include/linux/cdev.h中定义有这样一个数据结构.
struct cdev {
struct kobject kobj;//内嵌的一个kobject结构
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;//设备的设备号的
    unsigned int count;//设备引用计数器
};
这个数据结构就是内核中对字符设备的描述.内核还提供了一组用于操作该数据结构的函数:
struct cdev *cdev_alloc(void);
void cdev_init(struct cdev *, const struct file_operations *);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_put(struct cdev *p);
void cdev_del(struct cdev *);

以上函数和结构体都在include/linux/cdev.h声明,但是函数的实现是在fs/char_dev.h中.具体分析如下:
struct cdev *cdev_alloc(void)
{
    struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    if (p) {
        INIT_LIST_HEAD(&p->list);
        kobject_init(&p->kobj, &ktype_cdev_dynamic);
//在cdev_init也会有这样一个函数,不过这里的初始化会在最终执行时会释放上面申请
//的空间.而下面的那个函数最终执行时只会清空cdev这个结构体,而不释放.
    }
    return p;
}

/**
@cdev: 这个结构体就是已经申请好的struct cdev.
@fops: 是针对于这个设备所写的file_operations.
这个操作须在cdev_add()之前完成.
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{   
memset(cdev, 0, sizeof *cdev);//将cdev这个申请好的数据结构请0.
    INIT_LIST_HEAD(&cdev->list);//这个结构体见下面的解释4
kobject_init(&cdev->kobj, &ktype_cdev_default);//见解释5 解释6
    //这里主要是对cdev中kobject这个对象初始化成默认方式
    cdev->ops = fops;  //初始化操作方法
}
解释4:include/linux/list.h中.
struct list_head {
    struct list_head *next, *prev;
};
static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}
解释5:lib/kobject.h
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
    char *err_str;   
    if (!kobj) {
        err_str = "invalid kobject pointer!";        goto error;
    }
    if (!ktype) {
        err_str = "must have a ktype to be initialized properly!\n";
        goto error;
    }   
    if (kobj->state_initialized) {
        //* do not error out as sometimes we can recover
        printk(KERN_ERR "kobject (%p): tried to init an initialized "
               "object, something is seriously wrong.\n", kobj);
        dump_stack();//
    }
    kobject_init_internal(kobj);//解释A
    kobj->ktype = ktype;
    return;
error:
    printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
    dump_stack();//这个函数的理解看字面意思就可以了,我没有进行进一步的追查
}
解释A:
Kobject_init_internal
static void kobject_init_internal(struct kobject *kobj)
{
    if (!kobj)
        return;
    kref_init(&kobj->kref);
    INIT_LIST_HEAD(&kobj->entry);
    kobj->state_in_sysfs = 0;
    kobj->state_add_uevent_sent = 0;
    kobj->state_remove_uevent_sent = 0;
    kobj->state_initialized = 1;
}   
lib/kref.c
void kref_init(struct kref *kref) //struct kref {  atomic_t refcount; };
{  //这个kref的里面只有一项,它的定义:typedef struct { int counter; } atomic_t;
    kref_set(kref, 1);
}//
void kref_set(struct kref *kref, int num)
{   
atomic_set(&kref->refcount, num);//
    smp_mb();//设置内存障碍
}  
#define atomic_set(v,i)     (((v)->counter) = (i))

include/asm-x86/atomic_32.h
#define atomic_set(v,i)     (((v)->counter) = (i))

fs/char_dev.c
static struct kobj_type ktype_cdev_default = {
    .release    = cdev_default_release,
};
static void cdev_default_release(struct kobject *kobj)
{
struct cdev *p = container_of(kobj, struct cdev, kobj);//见解释B
    //由下面的分析解释可以轻松的看出,此处的p指向的是包含kobj这个结构的
//struct cdev结构体.
cdev_purge(p);//见解释C 有下面的解释可以看出这里是对cdev的清空操作.
}//下面都是源码分析解释
解释B:
#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \//1
(type *)( (char *)__mptr - offsetof(type,member) );})//2
container_of()宏的功能是什么?
A:    第1行:将__mptr转化成type结构中member类型.并且把ptr的地址给它
第2行:先将__mptr转换成字符型指针,再减去member在type中的偏移量.最后在将它装换成type类型.
B:    其中offsetof宏定义在[include/linux/stddef.h]中定义为:
C:    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
offsetof(type,member)是获取member在type中的偏移量.最后装换成整数值.
D:    其实最后的结果就是:指针ptr减去了一个偏移量.这里肯定ptr是指向了其它数据结构.即是ptr- offsetof(),这也就相当于指向了包含该ptr的type类型的结构体地址,并且强制将其转换为(type*)类型.
如下图:
指针ptr指向结构体type中的成员member;通过指针ptr,返回结构体type的起始地址。
           type
       |----------|
       |          |
       |          |
       |----------|
ptr->| member --|
       |----------|
       |          |
       |          |
       |----------|
解释C:于上面的在同一个文件中定义
static void cdev_purge(struct cdev *cdev)
{
    spin_lock(&cdev_lock);
    while (!list_empty(&cdev->list)) {//见解释D
        struct inode *inode;
        inode = container_of(cdev->list.next, struct inode, i_devices);
        list_del_init(&inode->i_devices);//见解释E
        inode->i_cdev = NULL;
    }
    spin_unlock(&cdev_lock);
}
解释D:inclued/linux/list.h
static inline int list_empty(const struct list_head *head)
{
    return head->next == head;//就是将这个链表清空
}
解释E:include/linux/list.h //将entry从链表中删除并从新初始化
static inline void list_del_init(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    INIT_LIST_HEAD(entry);
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
    next->prev = prev;
    prev->next = next;
}
Zhenwen Xu - Open and Free
Home Page:    http://dim4.cn
学长写的很详细,学习了!
我们也学了这部分,也可以与我们交流:
http://blog.chinaunix.net/group/group_1488.html
我也常常在看你们这里的
发新话题