IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 字符设备的注册(cdev_int()与cdev_add()) -> 正文阅读

[数据结构与算法]字符设备的注册(cdev_int()与cdev_add())

1.cdev_init()。初始化cdev对象的一些成员

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
    memset(cdev, 0, sizeof *cdev);
    INIT_LIST_HEAD(&cdev->list);
    kobject_init(&cdev->kobj, &ktype_cdev_default);
    cdev->ops = fops;
}

首先把传进来的cdev对象所占的内存清零

然后初始化cdev->list_head链表

接着把cdev->kobj也初始化,调用的kobject_init注释如下:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)    //@kobj :要被初始化的kobject
{                                                                   //@ktype:该kobj要初始化的ktype成员
    char *err_str;

    if (!kobj) {                                                    //如果kobj为空
        err_str = "invalid kobject pointer!";                       //打印"invalid kobject pointer!"
        goto error;
    }
    if (!ktype) {
        err_str = "must have a ktype to be initialized properly!\n";//如果ktype为空
        goto error;                                                 //打印"must have a ktype to be initialized properly!\n"
    }
    if (kobj->state_initialized) {                                  //如果kobj已经被初始化过了
        /* do not error out as sometimes we can recover */
        printk(KERN_ERR "kobject (%p): tried to init an initialized "   //打印错误信息:尝试初始化一个已经初始化过的kobject
               "object, something is seriously wrong.\n", kobj);
        dump_stack();                                                   //在控制台打印Oops信息
    }

    kobject_init_internal(kobj);                                    //kobj初始化
    kobj->ktype = ktype;                                            //把该kobj的ktype成员初始化为ktype
    return;                                                         //返回

error:
    printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);           //打印错误信息
    dump_stack();                                                   //在控制台打印Oops信息
}
EXPORT_SYMBOL(kobject_init);

其中的ktype_cdev_default就是kobject_init函数的第二个参数,ktype_cdev_default的定义如下:

static struct kobj_type ktype_cdev_default = {
    .release    = cdev_default_release,
};

kobj_type类型中的release()是在kobject释放时调用的,但是看了cdev_default_release的实现,发现并没有释放这个kobject,因为cdev不是动态创建的,注意看cdev_alloc,它在调用kobject_init的时候,kobj_type成员就变成了ktype_cdev_dynamic,它里面的release函数就释放了这个cdev对象。

接下来看cdev_default_release做了什么:

static void cdev_default_release(struct kobject *kobj)
{
    struct cdev *p = container_of(kobj, struct cdev, kobj);
    struct kobject *parent = kobj->parent;

    cdev_purge(p);
    kobject_put(parent);
}

首先获得了该kobj所在的cdev对象,然后获得了这个kobj的父对象。接着调用cdev_purge(p),这个cdev_purge()做了什么呢?

static void cdev_purge(struct cdev *cdev)
{
    spin_lock(&cdev_lock);
    while (!list_empty(&cdev->list)) {
        struct inode *inode;
        inode = container_of(cdev->list.next, struct inode, i_devices);
        list_del_init(&inode->i_devices);
        inode->i_cdev = NULL;
    }
    spin_unlock(&cdev_lock);
}

首先是上锁,然后一个while循环,循环的条件是cdev里面的list_head链表非空,循环体内,首先获得链表的下一个成员所在的inode对象,然后利用inode->i_devices(struct list_head类型),将该节点从所在的链表删除,最后把该inode的i_cdev赋空。这个while循环是将挂载在cdev的list_head链表的所有成员删除。

回到cdev_default_release,接下来调用了kobject_put(parent),这一操作是将该kobj的父对象的kref减一,如果kref==0,调用parent的kobj_type成员中的release函数将parent删除。

2.cdev_add()

?????? cdev_add函数主要是将cdev加入到cdev_map中,然后将cdev的kobject成员的parent对象的kref成员加1(有点绕),具体函数实现如下:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    int error;

    p->dev = dev;
    p->count = count;

    error = kobj_map(cdev_map, dev, count, NULL,
             exact_match, exact_lock, p);
    if (error)
        return error;

    kobject_get(p->kobj.parent);

    return 0;
}

该函数首先用设备号dev和count给p的成员赋值,接着调用kobj_map,函数实现如下:

int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
         struct module *module, kobj_probe_t *probe,
         int (*lock)(dev_t, void *), void *data)
{
    unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
    unsigned index = MAJOR(dev);
    unsigned i;
    struct probe *p;

    if (n > 255)
        n = 255;

    p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL);
    if (p == NULL)
        return -ENOMEM;

    for (i = 0; i < n; i++, p++) {
        p->owner = module;
        p->get = probe;
        p->lock = lock;
        p->dev = dev;
        p->range = range;
        p->data = data;
    }
    mutex_lock(domain->lock);
    for (i = 0, p -= n; i < n; i++, p++, index++) {
        struct probe **s = &domain->probes[index % 255];
        while (*s && (*s)->range < range)
            s = &(*s)->next;
        p->next = *s;
        *s = p;
    }
    mutex_unlock(domain->lock);
    return 0;
}

首先是kobj_map成员,内核定义了一个kobj_map类型的对象cdev_map。kobj_map的定义如下:

struct kobj_map {
    struct probe {
        struct probe *next;
        dev_t dev;
        unsigned long range;
        struct module *owner;
        kobj_probe_t *get;
        int (*lock)(dev_t, void *);
        void *data;
    } *probes[255];
    struct mutex *lock;
};

probes也是一个哈希表,实现原理和设备号的哈希表chrdevs几乎完全一样。下面看具体实现。

?????? 首先计算主设备号个数,和register_chrdev_region中一样,防止range过大,分配的主设备号不止一个。然后定义一个index等于主设备号,申明一个struct probe类型的指针,调用kmalloc_array为p分配内存,如果p还为空,返回错误代码,接下来用传入的参数为p的成员初始化。接着调用cdev_map中的互斥锁上锁,接下来把p插入哈希表probes中,插入规则还是range小的在前。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-26 12:18:24  更:2021-07-26 12:19:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:49:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码