【整理】--cdev_init()/cdev_alloc(),cdev_add(),cdev_del()

(1)

内核中每个字符设备都对应一个 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;       // 设备范围号大小

};

(2)

初始化的两种方式:cdev_init() , cdev_allon()

一个 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); 注1;

INIT_LIST_HEAD(&cdev->list);

kobject_init(&cdev->kobj, &ktype_cdev_default);

cdev->ops = fops;

}

由此可见,两个函数完成的功能基本一致,只是 cdev_init()还多赋了一个 cdev->ops的值。

这里需要注意的是kzalloc后的空间是不需要再执行memset的,因为它本身就包含了这个操作。而memset一般作用在已经存在的空间上。

由此基本上对这两个函数有了一个基本的概念:cdev_alloc函数针对于需要空间申请的操作,而cdev_init针对于不需要空间申请的操作;因此如果你定义的是一个指针,那么只需要使用cdev_alloc函数并在其后做一个ops的赋值操作就可以了;如果你定义的是一个结构体而非指针,那么只需要使用cdev_init函数就可以了。

看到有些代码在定义一个指针后使用了cdev_alloc函数,紧接着又使用了cdev_init函数,这个过程不会出现错误,但只是做了一些重复的无用工作,其实完全可以不需要的。

(3)

初始化cdev后,需要把它添加到系统中去。为此可以调用 cdev_add()函数。传入 cdev结构的指针,起始设备编号,以及设备编号范围。

Synopsis

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

Arguments

  p  : the cdev structure for the device

  dev  :  the first device number for which this device is responsible

  count  :  the number of consecutive minor numbers corresponding to this device

Description

  cdev_add adds the device represented by p to the system, making it live immediately. A negative error code is returned on failure.

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()函数就不展开了,我只是大致讲一下它的原理。内核中所有的字符设备都会记录在一个 kobj_map结构的 cdev_map变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。kobj_map()函数就是用来把字符设备编号和 cdev结构变量一起保存到 cdev_map这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup()函数,根据设备编号就可以找到cdev结构变量,从而取出其中的ops字段。

(4)

当一个字符设备驱动不再需要的时候(比如模块卸载),就可以用 cdev_del()函数来释放 cdev占用的内存。

Name

  cdev_del — remove a cdev from the system

Synopsis

  void fsfunc cdev_del( struct cdev * p);

Arguments

  p : the cdev structure to be removed

Description

cdev_del removes p from the system, possibly freeing the structure itself.

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结构本身。

注1:

Memset  用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘ ’或‘/0’;

例:chara[100];memset(a, ‘/0‘, sizeof(a));

memcpy  用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度。

例:chara[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a),会造成b的内存地址溢出。

Strcpy   就只能拷贝字符串了,它遇到‘/0‘就结束拷贝。

例:chara[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串长度(第一个‘/0’之前)是否超过50位,如超过,则会造成b的内存地址溢出。

memset主要应用是初始化某个内存空间。

memcpy是用于copy源空间的数据到目的空间中。

strcpy用于字符串copy,遇到‘/0’,将结束。

时间: 2024-10-23 10:09:54

【整理】--cdev_init()/cdev_alloc(),cdev_add(),cdev_del()的相关文章

转:1.1 cdev_init cdev_alloc 使用说明

对 “从globalmem学习linux字符设备驱动” 的 cdev_init 和 cdev_alloc中一些不清楚的地方进行说明: cdev_init 和 cdev_alloc函数定义如下: 1.1 cdev_init 函数用于初始化cdev的成员,并建立生cdev和file_operations之间连接 /fs/char_dev.c 2.6.35 550 void cdev_init(struct cdev *cdev, const struct file_operations *fops)

【原创】--【字符设备】--全流程

一.概述 1.字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等.2.块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备. 二.设备使用流程:1.生成设备号,dev_t 一个字符设备

【驱动】Linux初级驱动系列框架

[系统环境搭建] 1.uboot的命令 set serverip 192.168.7.xx set ipaddr 192.168.7.xxx set bootcmd tftp 20800000 zImage\;bootm 20800000 //开发模式 set bootcmd nand read 20800000 40000 2000000\;bootm 20800000 //产品模式 set bootargs root=/dev/nfs nfsroot=192.168.7.xx:/opt/ro

linux驱动之字符设备

第一部分:字符设备工作过程1.系统调用和驱动程序的关联关键结构体:struct file_operation:file_operation结构体的每一个成员的名字都对应着一个系统调用.用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数.这是linux的设备驱动程序工作的基本原理.编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域.

misc设备

WatchDog Timer驱动 混杂设备 Misc(或miscellaneous)驱动是一些拥有着共同特性的简单字符设备驱动.内核抽象出这些特性而形成一些API(在文件drivers/char/misc.c中实现),以简化这些设备驱动程序的初始化.所有的misc设备被分配同一个主设备号MISC_MAJOR(10),但是每一个可以选择一个单独的次设备号.如果一个字符设备驱动要驱动多个设备,那么它就不应该用misc设备来实现. 通常情况下,一个字符设备都不得不在初始化的过程中进行下面的步骤: 通过

驱动开发 —— 高级驱动学习方法

回顾一下之前的字符设备驱动编程: 1,实现入口函数 xxx_init()和卸载函数 xxx_exit() 2,申请设备号 register_chrdev_region (与内核相关) 3,注册字符设备驱动 cdev_alloc.cdev_init. cdev_add (与内核相关) 4,利用udev/mdev机制创建设备文件(节点) class_create, device_create (与内核相关) 5,硬件部分初始化 io资源映射 ioremap,内核提供gpio库函数 (与硬件相关) 注

Android深度探索--HAL与驱动开发----第七章读书笔记

首先创建led驱动的设备文件,可以使用cdev_init,register_chrdev_region,cdev_add等建立主设备号的设备文件.步骤如下: 1使用cdev_init初始化cdev 2指定设备号 3使用cdev_add函数将字符设备添加到内核中的字符设备组中 4使用class_creat宏创建struct class 5使用device_creat函数创建设备文件 然后卸载led驱动的设备文件 {Device_destroy(leds_class,dev_number); If(

第七章感想

     本章学习控制发光二极管,通过直接与硬件交互完成,控制开发板上的四个小灯.      LED驱动的实现原理可以开始编写led驱动了,与上一章步骤类似,通过命令和读写文件实现.首先创建led驱动的设备文件,可以使用cdev_init,register_chrdev_region,cdev_add等建立主设备号的设备文件.步骤如下:1使用cdev_init初始化cdev2指定设备号3使用cdev_add函数将字符设备添加到内核中的字符设备组中4使用class_creat宏创建struct c

LDD3 字符设备驱动简单分析

最近在看LDD3,理解了一下,为了加深自己的印象,自己梳理一下.我用的CentOS release 6.6 (Final)系统. 一.编写编译内核模块的Makefile 以下是我用的Makefile ifneq ($(KERNELRELEASE),) # 第一次被调用时,KERNELRELEASE为空,所以不会被执行 obj-m := scull.o else # 将内核编译用的Makefile路径赋值给KERNELRELEASE KERNELRELEASE ?= /lib/modules/`u