内核中每个字符设备都对应一个 cdev 结构的变量,下面是它的定义:

linux-2.6.22/include/linux/cdev.h
struct cdev {
   struct kobject kobj;          // 每个 cdev 都是一个 kobject
   struct module *owner;       // 指向实现驱动的模块
   const struct file_operations *ops;   // 操纵这个字符设备文件的方法
   struct list_head list;       // 与 cdev 对应的字符设备文件的 inode->i_devices 的链表头
   dev_t dev;                   // 起始设备编号
   unsigned int count;       // 设备范围号大小
};

一个 cdev 一般它有两种定义初始化方式:静态的和动态的。
静态内存定义初始化:
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;

动态内存定义初始化:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &fops;
my_cdev->owner = THIS_MODULE;

两种使用方式的功能是一样的,只是使用的内存区不一样,一般视实际的数据结构需求而定。

下面贴出了两个函数的代码,以具体看一下它们之间的差异。
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);
   }
   return p;
}

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_init() 还多赋了一个 cdev->ops 的值。

初始化 cdev 后,需要把它添加到系统中去。为此可以调用 cdev_add() 函数。传入 cdev 结构的指针,起始设备编号,以及设备编号范围。
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
   p->dev = dev;
   p->count = count;
   return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map
变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到
cdev_map 这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev
结构变量,从而取出其中的 ops 字段。

kobj_map函数中哈希表的实现原理和前面注册分配设备号中的几乎完全一样,通过
要加入系统的设备的主设备号major(major=MAJOR(dev))来获得probes数组的索引值i(i = major %
255),然后把一个类型为struct probe的节点对象加入到probes[i]所管理的链表中,如图2-6所示。其中struct
probe所在的矩形块中的深色部分是我们重点关注的内容,记录了当前正在加入系统的字符设备对象的有关信息。其中,dev是它的设备号,range是从
次设备号开始连续的设备数量,data是一void
*变量,指向当前正要加入系统的设备对象指针p。图2-6展示了两个满足主设备号major % 255 =
2的字符设备通过调用cdev_add之后,cdev_map所展现出来的数据结构状态。

 
图2-6  通过cdev_add向系统中加入设备

所以,简单地说,设备驱动程序通过调用cdev_add把它所管理的设备对象的指针嵌入到一个类型为struct probe的节点之中,然后再把该节点加入到cdev_map所实现的哈希链表中。

对系统而言,当设备驱动程序成功调用了cdev_add之后,就意味着一个字符设备对象已经加入到了系统,在需要的时候,系统就可以找到它。对用户态的程序而言,cdev_add调用之后,就已经可以通过文件系统的接口呼叫到我们的驱动程序。

当一个字符设备驱动不再需要的时候(比如模块卸载),就可以用 cdev_del() 函数来释放 cdev 占用的内存。
void cdev_del(struct cdev *p)
{
   cdev_unmap(p->dev, p->count);
   kobject_put(&p->kobj);
}
其中 cdev_unmap() 调用 kobj_unmap() 来释放 cdev_map 散列表中的对象。kobject_put() 释放 cdev 结构本身。

cdev_系列函数的更多相关文章

  1. PHP进程通信基础——shmop 、sem系列函数使用

    PHP进程通信基础--shmop .sem系列函数使用 进程通信的原理就是在系统中开辟出一个共享区域,不管是管道也好,还是共享内存,都是这个原理.如果心中有了这个概念,就会很方便去理解代码.由于官网上 ...

  2. PHP 使用 curl_* 系列函数和 curl_multi_* 系列函数进行多接口调用时的性能对比

    在页面中调用的服务较多时,使用并行方式,即使用 curl_multi_* 系列函数耗时要小于 curl_* 系列函数. 测试环境 操作系统:Windows x64 Server:Apache PHP: ...

  3. 第8章 用户模式下的线程同步(1)_Interlocked系列函数

    8.1 原子访问:Interlocked系列函数(Interlock英文为互锁的意思) (1)原子访问的原理 ①原子访问:指的是一线程在访问某个资源的同时,能够保证没有其他线程会在同一时刻访问该资源. ...

  4. PHP ob系列函数详解

    一. 相关函数简介:    1.Flush:刷新缓冲区的内容,输出.    函数格式:flush()    说明:这个函数经常使用,效率很高.    2.ob_start :打开输出缓冲区    函数 ...

  5. Linux中exec()执行文件系列函数的使用说明

    函数原型: 描述:    exec()系列函数使用新的进程映像替换当前进程映像.    工作方式没有什么差别, 只是参数传递的方式不同罢了. 说明:    1. 这6个函数可分为两大类: execl( ...

  6. php Output Control 函数 ob_系列函数详解

    <?php /* * 输出缓冲控制 * * flush — 刷新输出缓冲 ob_clean — 清空(擦掉)输出缓冲区 ob_end_clean — 清空(擦除)缓冲区并关闭输出缓冲 ob_en ...

  7. 从简单需求到OLAP的RANK系列函数

    同事问了一个非常简单的问题,怎么取出每个partition里面另外一个列的最小值? create table t1 (int c1, int c2);   假如按照c2分区,0-10,10-20,20 ...

  8. PHP 载入图像 imagecreatefrom_gif_jpeg_png 系列函数

    imagecreatefrom 系列函数用于从文件或 URL 载入一幅图像. 载入图像 imagecreatefrom 系列函数用于从文件或 URL 载入一幅图像,成功返回图像资源,失败则返回一个空字 ...

  9. PHP中ob系列函数整理

    ob,输出缓冲区,是output buffering的简称,而不是output cache.ob用对了,是能对速度有一定的帮助,但是盲目的加上ob函数,只会增加CPU额外的负担. 下面我说说ob的基本 ...

随机推荐

  1. VMware系统运维(六)VMware vSphere Web Client安装

    1.开始安装VMware vSphere Web Client 2.下一步 3.接受协议,下一步,大哥求你了,下次直接将这个默认下一步吧,嘿嘿 4.选择安装位置,下一步 5.配置端口号,默认9090和 ...

  2. Windows 7 无线网络故障

    朋友打电话来问为何他的PC不能连接到家里的无线网络,而手机等其他设备都可以?相互交谈之中,我问如下几个问题: 是否可以搜索到其他无线网络? 答:是.(想确实无线网卡工作是否正常) 新的手机是否可以连接 ...

  3. ASP.NET MVC 4框架揭秘(微软6任MVP,高级软件顾问蒋金楠新作)

    http://www.cnblogs.com/artech/

  4. 网络请求的null值处理

    最近项目中经常有遇到从服务器请求的数据是null的情况,这种情况下如果用[dic objectForKey:@"key"]方法,程序会发生崩溃现象,因为项目是以前的老项目,而且有太 ...

  5. 搭建Cocos Code IDE开发环境

    Cocos Code IDE是Cocos2d-x团队开发的,用于开发Cocos2d-JS和Cocos2d-x Lua绑定的游戏工具,它是基于Eclipse[ Eclipse 是一个开放源代码的.基于J ...

  6. C# 中 datagridview 绑定BindingList类型和更新

    C# 中的datagridview是一个非常有用且强大的控件,可以用来绑定数据库.绑定LIST类型的变量等等. 这里我们说一说绑定List类型并实时更新datagridview的情况.实时更新,指的是 ...

  7. 20141124-HTML-JavaScrilpt

    JavaScript JavaScript(简称JS),他是一门HTML的脚本语言,用来改进设计.验证表单.检测浏览器.创建cookies,以及更多的应用. 他的用法及语法类似于Visual Stud ...

  8. path 环境变量

    path(环境变量)是dos以前的内部命令,windows继续沿用至今.用作运行某个命令的时候,本地查找不到某个命令或文件,会到这个声明的目录中去查找.一般设定java的时候为了在任何目录下都可以运行 ...

  9. Struts2+Hibernate4+Spring4整合

    jar包 配置文件 web.xml文件 <!-- needed for ContextLoaderListener --> <context-param> <param- ...

  10. gcc与g++的区别

    一:gcc与g++比较 编译c/c++代码的时候,有人用gcc,有人用g++,于是各种说法都来了,譬如c代码用gcc,而 c++代码用g++,或者说编译用gcc,链接用g++,一时也不知哪个说法正确, ...