linux驱动(第十二课,CDEV)

CDEV是linux中最庞大的一类设备。

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

struct file_operations{
	struct module* owner;
	...
	(*open)(...),
	(*release)(...),
	(*read)(...),
	(*write)(...),
	(*ioctl)(...),
	(*llseek)(...),
	(*poll)(...),
	(*mmap)(...),
	...
};

CDEV内嵌一个链节,所以CDEV可以构成链表。
CDEV内嵌一个kobject,所以它扩展了kobject,支持SYSFS文件系统访问机制。
它的几个重要成员是,dev, ops。
其中dev是设备号,ops是关联的文件操作集。可以看到,ops里,是一系列函数指针。

来看看内核中FILE是怎么定义的。

struct file{
	...
	struct path f_path;
	struct inode * f_inode;
	const struct file_operations * f_ops;
	loff_t f_pos;

	void* private_data;
	...
};

struct i_node{
	...
	unsigned long i_type;
	struct cdev* i_cdev;
	struct block_device* i_bdev;
	...
};

从中可以看出,INODE记录了类型,并关联到一个CDEV。
而FILE内嵌一个path,用来记录FILE在文件系统中的路径信息。
FILE关联到一个INODE,用来索引打开的文件在内核中的INODE。
FILE关联到一个FOPS,当内核为FILE提供文件操作服务时,会调用FOPS中的函数完成实际操作。
FILE的private_data,是一个通配句柄。通常用来做实体标签(InstanceTag)。
InstanceTag是SOD中常用的设计技巧。
在一个对象中,如果需要另外一个对象作为成员,有两种方式,一种是内嵌,另一种是关联引用。如果另一个对象占用空间很大,或者另一个对象也可能被其他的对象关联引用时,我们就不能使用内嵌方式,而应该使用关联引用的方式。
这就是InstanceTag。
通过在对象中放置一个指针,既可以缩小体积,又能够索引到所关联的另外一个对象。
很多时候,如果对象是动态生成的,那么就更应该使用InstanceTag。

我们来看一看File Operate Mechanism With CDEV。
kernel维护一个ACTIVE_CDEV_LIST,所有注册到内核中的CDEV,被添加到ACTIVE_CDEV_LIST中。
kernel维护一个INODE_TREE,如果一个INODE对应的是CDEV,那么当用户进程通过path索引到一个INODE_CDEV时,打开的FILE就关联到这个CDEV。
整个索引过程为,filep->f_inode->i_cdev->ops.
此时,内核会把CDEV的FOPS传递给FILE。这样,FILE就有了有效的FOPS。
FILE在open时,首先索引到CDEV,然后获取有效 的FOPS填充FILE的成员,最后,会调用CDEV的FOPS中的open函数。这里是进行实体标记的最佳位置,通常会用CDEV的衍生对象的句柄来填充filp->private_data。

我们可以看到,FOPS中的函数,首参都是FILE。
这是符合SOD设计思想的。object has method。
在linux环境下,没有OOD的语法表达形式。但是首参,却可以间接代表object。
当用户进程发起文件操作相关的调用时,首参是FILE,表示这个调用栈,是面向FILE的调用栈。后续的层层调用,都按约定,传入的首参为FILE,也就是表明这一系列的调用,都隶属于同一个调用栈。
虽然没有语法上的规定,表明非要这样去设计函数,但是,遵循SOD设计思想去设计函数,将使得程序的逻辑更易懂。
另外,FILE是一个完备的控制块,利用FILE,可以获取所需要的任何系统资源。

在open中配置好FILE中的InstanceTag之后,其他的事务性函数被内核调用时,都能够通过filp->private_data索引到Instance,找到Derived_CDEV。

整个驱动分为几个部分:
1)Derived_CDEV_CB定义,并实例化。
2)FOPS实例化,并填充。
3)驱动操作函数编写,分为机制性函数的编写和事务性函数的编写。
4)模块加载函数编写,在其中注册CDEV。
5)模块卸载函数编写,在其中反操作。
内核提供了CDEV注册相关的API。

alloc_chrdev_region,
cdev_init,
MKDEV,MAJOR, MINOR,
cdev_add,
cdev_del,

由于驱动运行在内核空间,而用户buf位于用户空间,所以内核提供了跨空间拷贝API。

copy_from_user,
copy_to_user,
put_user,
get_user

BASH命令mknod用来创建一个path对应的INODE
#mknod /dev/vser0 c 256 0
#mknod /dev/vser1 c 256 1

但是通常是由mdev管理设备节点,所以不需要手工写入mknod命令。
另一个方法是,在rcS启动脚本中,写入mknod命令。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值