一个简单的字符设备驱动
拿朋友的作业,简单写个小 demo。
需求
Write a device driver for a character device which implements a simple way of message passing. The kernel maintains a list of messages. To limit memory usage, we impose a limit of 4KB = 4*1024 bytes for each message and also impose a limit of the total number of messages stored in the kernel, which is 1000.
Your device driver should perform the following operations:
- When the module is loaded, the device is created. An empty list of messages is created as well.
- Removing the module deallocates all messages, removes the list of messages and removes the device.
- Reading from the device returns one message, and removes this message from the kernel list. If the list of messages is empty, the reader returns -EAGAIN.
- Writing to the device stores the message in kernel space and adds it to the list if the message is below the maximum size, and the limit of the number of all messages stored in the kernel wouldn't be surpassed with this message. If the message is too big, -EINVAL is returned, and if the limit of the number of all messages was surpassed, -EBUSY is returned.
- The kernel module which implements this driver must be called charDeviceDriver.ko.
You need to ensure that your code deals with multiple attempts at reading and writing at the same time. Moreover, your code should handle several read and write attempts concurrently. Your critical sections should be as short as possible. The reader should obtain the messages in a FIFO (first in first out) manner.
简单来说,就是要求写一个字符设备的驱动程序,实现 FIFO 模式的消息读写,且支持并发操作。
思路
概念上没啥复杂的,用一个单向链表就能实现,主要是写的时候有不少细节需要注意,不要漏加锁/放锁,不要忘记释放内存,尤其是在异常情况下。
编写设备驱动基本上就是实现以下几个函数:
init_module
:加载驱动时调用,对应insmod
命令,通过register_chrdev()
分配主设备号,同时在这里初始化消息链表cleanup_module
:卸载驱动时调用,对应rmmod
命令,在这里要释放资源并unregister_chrdev()
device_open
:打开设备文件时调用,增加该模块的引用计数device_release
:关闭设备文件时调用,减少该模块的引用计数device_read
:读取设备文件时调用,从链表头弹出一条消息device_write
:写入设备文件时调用,向链表尾插入一条消息
因为涉及到一些原来不知道的知识点,踩了一些坑,主要是:
- 内核直接访问用户内存会 crash 导致系统重启,需要先通过
copy_to_user()
、copy_from_user()
、get_user()
、put_user()
这些函数拷贝到内核空间中才能使用 - 内核中申请内存要用
kmalloc()
,对应的释放函数是kfree()
实现
这里贴一下之前提到的几个函数的实现,完整代码及测试用例见 Github 仓库。
1 | /* |