本文共 2614 字,大约阅读时间需要 8 分钟。
4.1 scull的设计
scull字符设备的驱动将默认对应4个scull设备(文件),分别为scull0到scull3。开发者可以通过在编译时、加载时或运行时的不同方式来改变scull设备的数量。后续将详细说明每种方式的具体操作。
4.2 字符设备的主、次设备号
字符设备的存取依赖于其在文件系统中的名称,类似于普通文件的存取。通过ls -l /dev
命令可以查看/dev
目录中的设备信息。第1列的字母表示设备类型(c表示字符设备),第5列的数字分别表示主设备号和次设备号。主设备号用于连接设备驱动,现代Linux系统通常允许多个驱动共享同一主设备号,但大部分情况下一个主设备号对应一个驱动。
4.2.1 设备号的内部表示
设备号在内核中用dev_t
类型表示。随着内核版本的变化,设备号的实际类型和主次设备号的位分布也可能发生变化。因此,不能简单地将设备号视为某个特定类型。头文件<linux/kdev_t.h>
提供了辅助宏来管理设备号:
MAJOR(dev_t dev)
:获取设备的主设备号MINOR(dev_t dev)
:获取设备的次设备号MKDEV(int major, int minor)
:构造dev_t
,根据指定的主次设备号4.2.2 设备号的分配与释放
设备号由内核统一管理,字符设备驱动需要向系统申请设备号。分配方式分为静态和动态两种:
静态申请设备号:调用register_chrdev_region
函数,参数first
为申请的第一个设备号,count
为要申请的连续设备号个数,name
为设备号区间的名称。函数返回0表示成功,负数表示失败。
动态申请设备号:调用alloc_chrdev_region
函数,参数dev
指向申请返回的设备号,firstminor
指定第一个从设备号,count
为要申请的设备号个数,name
为设备号区间的名称。函数返回0表示成功,负数表示失败。
设备号申请后必须及时释放,通常在设备驱动模块的退出函数中调用unregister_chrdev_region
函数。
4.2.3 动态分配主设备号
部分主设备号已被预先分配给通用设备,开发者可以通过查看Documentation/devices.txt
文档获取预分配的主设备号。动态分配方式的优点是可以灵活获取设备号,但需要确保设备号的唯一性和可用性。通过/proc/devices
文件可以查看当前分配的主设备号。
4.2.4 scull模块的加载与卸载
为了测试scull驱动,通常需要手动加载或卸载scull模块。开发者可以使用脚本实现:
#!/bin/shinsmod ./scull.komknod /dev/scull0 c `awk "$module" { print $1 }" /proc/devices | head -n1`mknod /dev/scull1 c $major 1mknod /dev/scull2 c $major 2mknod /dev/scull3 c $major 3chmod -sf scull0 /dev/scull0chgrp bill /dev/scull[0-3]
#!/bin/shrmmod scullrm -f /dev/scull[0-3]
另外,Linux提供了模块的自动加载机制。开发者可以通过以下步骤实现:
sudo cp scull.ko /lib/modules/`uname -r`/kernel/drivers/char/scull.ko
/etc/modules
文件中添加模块名称:sudo vi /etc/modules
添加行:
scull
lsmod
#!/bin/shmodprobe scull
并创建脚本的软链接:
sudo ln -s /etc/init.d/scull /etc/rcS.d/Sscull
4.3 字符设备的重要数据结构
在开发字符设备驱动前,需要了解以下关键数据结构:
struct file_operations
:定义在<linux/fs.h>
中,描述文件操作接口。字符设备通常将大多数成员置为NULL
,如readdir
、poll
等,因为这些操作不适用于字符设备。struct file
:定义在<linux/fs.h>
中,描述打开的文件描述符。成员包括: f_mode
:文件模式(读写权限)。f_flags
:文件标志(非阻塞、同步等)。f_op
:文件操作接口。f_private_data
:文件自定义数据。struct inode
:定义在<linux/fs.h>
中,描述文件系统中的文件节点。成员包括: i_rdev
:设备文件的设备号。i_bdev
:块设备指针。i_cdev
:字符设备指针。4.4 字符设备注册
4.4.1 字符设备的分配与初始化
字符设备通过struct cdev
结构体描述。可以通过静态或动态方式分配字符设备结构:
struct cdev *my_cdev = cdev_alloc();my_cdev->ops = &my_fops;
cdev_init(&my_cdev, &my_fops);
4.4.2 挂接字符设备到内核
调用cdev_add
函数将字符设备挂接到内核:
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
注意:字符设备在被挂接后可能立即进入使用,必须确保所有准备工作完成。
4.4.3 从内核删除字符设备
调用cdev_del
函数移除字符设备:
void cdev_del(struct cdev *p);
4.5 scull字符设备的实现
scull字符设备是对内存的操作,不依赖于具体硬件,因此可以在任何支持虚拟化的操作系统上测试。其核心功能包括:
通过以上步骤,可以实现一个功能完善的scull字符设备驱动。
转载地址:http://cqkfk.baihongyu.com/