《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: c
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
This is, on the surface, a book about writing device drivers for the Linux system. That is a worthy goal, of course; the flow of new hardware products is not likely to slow down anytime soon, and somebody is going to have to make all those new gadgets work with Linux. But this book is also about how the Linux kernel works and how to adapt its workings to your needs or interests. Linux is an open system; with this book, we hope, it is more open and accessible to a larger community of developers. This is the third edition of Linux Device Drivers. The kernel has changed greatly since this book was first published, and we have tried to evolve the text to match. This edition covers the 2.6.10 kernel as completely as we are able. We have, this time around, elected to omit the discussion of backward compatibility with previous kernel versions. The changes from 2.4 are simply too large, and the 2.4 interface remains well documented in the (freely available) second edition. This edition contains quite a bit of new material relevant to the 2.6 kernel. The discussion of locking and concurrency has been expanded and moved into its own chapter. The Linux device model, which is new in 2.6, is covered in detail. There are new chapters on the USB bus and the serial driver subsystem; the chapter on PCI has also been enhanced. While the organization of the rest of the book resembles that of the earlier editions, every chapter has been thoroughly updated. We hope you enjoy reading this book as much as we have enjoyed writing it.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值