《Linux Device Driver》——字符设备驱动程序

本文详细介绍了Linux字符设备驱动程序scull的设计与实现,包括scull设备的分类、主设备号和次设备号的管理、内核中的关键数据结构如file_operations、file、inode,以及字符设备的注册、open、release方法。scull使用内核内存管理函数动态分配内存,并通过ioctl接口调整量子和量子集尺寸。
摘要由CSDN通过智能技术生成

本章目标是编写一个完整的字符设备驱动程序——scull。即“simply character unility for loading localites,区域装载的简单字符设备”。scull是一个操作内存区域的字符设备驱动程序,这片内存就相当于一个设备。scull的优点在于不和硬件相关,而只是操作从内核中分配的一些内存。


scull的设计

scull0~scull3
 这四个设备分别由一个全局且持久的内存区域组成。
  “全局”:如果设备被多次打开,则打开它的所有文件描述符可共享该设备所包含的数据。
  “持久”:如果设备关闭后再打开,则其中数据不会丢失。

scullpipe0~scullpipe3
 这四个FIFO(先入先出)设备与管道类似。
 如果多个进程读取同一个设备,它们就会为数据发生竞争。

scullsingle
scullpriv
sculluid
scullwuid
 sculluid和scullwuid可以被多次打开,但每次只能由一个用户打开;如果另一个用户锁定了该设备,sculluid将返回“Device Busy”的错误,而scullwuid则实现阻塞式open。


主设备号和次设备号

对字符设备的访问通过文件系统内的设备名称进行。位于/dev下
ls -al
字符设备用“c“标识
在这里插入图片描述

块设备用”b“标识
在这里插入图片描述
主设备号表示设备对应的驱动程序;次设备号由内核使用,用于正确确定设备文件所指的设备。
现代Linux内核允许多个驱动程序共享主设备号,但大多还是“一个主设备号对应一个驱动程序”。


设备编号的内部表达

内核用dev_t类型(<linux/types.h>)来保存设备编号,dev_t是一个32位的数,12位表示主设备号,20为表示次设备号。
使用<linux/kdev_t.h>中定义的宏来转换格式。

转换
(dev_t)–>主设备号 MAJOR(dev_t dev)
(dev_t)–>次设备号 MINOR(dev_t dev)
主设备号、次设备号–>(dev_t) MKDEV(int major,int minor)

2.6内核之前只允许255个主设备号和255个次设备号。


分配和释放设备编号

#include<linux/fs.h>

操作 函数 返回值
静态指定 int register_chrdev_region(dev_t from, unsigned count, const char *name); 0成功;负数失败
动态分配 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name); 0成功;负数失败
释放 void unregister_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

参数说明:

/* fs/char_dev.c */
int register_chrdev_region(dev_t from, unsigned count, const char *name)
//from:	要分配的设备编号范围内的起始值
//count:	所请求连续设备编号的个数
//name:	和该编号范围关联的设备名称,将出现在/proc/devices和sysfs中
{
   
        struct char_device_struct *cd;
        dev_t to = from + count;
        dev_t n, next;

        for (n = from; n < to; n = next) {
   
                next = MKDEV(MAJOR(n)+1, 0);
                if (next > to)
                        next = to;
                cd = __register_chrdev_region(MAJOR(n), MINOR(n),
                               next - n, name);
                if (IS_ERR(cd))
                        goto fail;
        }
        return 0;
fail:
        to = n;
        for (n = from; n < to; n = next) {
   
                next = MKDEV(MAJOR(n)+1, 0);
                kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
        }
        return PTR_ERR(cd);
}

/* fs/char_dev.c */
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
//dev:			输出的参数,在完成调用后将保存已分配范围的第一个编号
//baseminor:	被请求的第一个次设备号
//count:		被请求的次设备号数目
//name:		和该编号范围关联的设备名称,将出现在/proc/devices和sysfs中
{
   
	struct char_device_struct *cd;
	cd = __register_chrdev_region(0, baseminor, count, name);
	if (IS_ERR(cd))
		return PTR_ERR(cd);
	*dev = MKDEV(cd->major, cd->baseminor);
	return 0;
}

/* fs/char_dev.c */
void unregister_chrdev_region(dev_t from, unsigned count)
//from:	要释放的设备编号范围内的起始值
//count:	要释放的设备的数目
{
   
        dev_t to = from + count;
        dev_t n, next;

        for (n = from; n < to; n = next) {
   
                next = MKDEV(MAJOR(n)+1, 0);
                if (next > to)
                        next = to;
                kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
        }
}

动态分配设备号

驱动程序应该始终使用alloc_chrdev_region而不是register_chrdev_region函数。

动态分配的缺点:由于分配的主设备号不能保证始终一致,所以无法预先创建设备节点。但是一旦分配了设备号,可以通过/proc/devices中读取得到。

动态设备号并不是随机生成的,如果不受其他(动态)模块影响,可以预期获得相同的动态主设备号。

分配之设备号的最佳方式是:默认采用动态分配,同时保留在加载甚至是编译时指定主设备号的余地。

以下是在scull.c中用来获取主设备号的代码:

 if (scull_major) {
   
                dev = MKDEV(scull_major, scull_minor);
                result = register_chrdev_region(dev, scull_nr_devs, "scull");
        } else {
   
                result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
                                "scull");
                scull_major = MAJOR(dev);
        }
        if (result < 0) {
   
                printk(KERN_WARNING "scull: can't get ma
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值