Linux字符设备驱动简介
在Linux系统中,设备驱动是操作系统的重要组成部分,负责在用户程序和硬件设备之间提供接口。设备驱动可以分为字符设备驱动和块设备驱动,本文将重点介绍字符设备驱动的基本概念和实现方法。
1. 字符设备驱动概念
字符设备是指那些以字符为单位进行数据传输的设备,例如键盘、串口和其它一些类似的设备。与之相对的是块设备(如硬盘驱动器),它以数据块为单位进行传输。
2. 字符设备文件和设备号
在Linux中,所有的设备都被抽象为文件,通过对文件的读写来操作设备。字符设备文件通常位于/dev
目录下。每个设备文件都关联到一个唯一的设备号,设备号包括主设备号和次设备号。主设备号用于标识设备类型,次设备号用于标识同类设备中的不同设备。
3. 注册字符设备驱动
要在Linux内核中实现字符设备驱动,首先需要注册设备。以下是常用的注册和注销设备的函数:
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
void unregister_chrdev(unsigned int major, const char *name);
register_chrdev
用于注册设备,它需要设备号、设备名称和一个指向file_operations
结构体的指针。file_operations
结构体包含了指向设备操作函数的指针。
4. file_operations
结构体
file_operations
是设备操作函数的集合,示例如下:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
// 更多操作...
};
要深入理解字符设备驱动的工作原理及其在Linux内核中的实现,我们需要通过一个具体的示例来展开详细讨论。在这个示例中,我们会创建一个简单的字符设备驱动并展示如何加载驱动模块、创建设备文件,并通过常规文件操作函数如open
, read
, write
, 和 close
来测试设备驱动的功能。
5. 创建字符设备驱动
首先,我们需要编写字符设备驱动的代码。以下是一个简单的示例,该驱动仅实现了open
, read
, write
, 和 close
方法:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "simple_char_dev"
#define CLASS_NAME "simple_char"
static int majorNumber;
static struct class* charClass = NULL;
static struct cdev charCdev;
// Device open function
static int dev_open(struct inode *inodep, struct file *filep){
printk(KERN_INFO "SimpleChar: Device has been opened\n");
return 0;
}
// Device read function
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
printk(KERN_INFO "SimpleChar: Device read\n");
return 0; // Bytes read
}
// Device write function
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
printk(KERN_INFO "SimpleChar: Received %zu characters from the user\n", len);
return len;
}
// Device close function
static int dev_close(struct inode *inodep, struct file *filep){
printk(KERN_INFO "SimpleChar: Device successfully closed\n");
return 0;
}
static struct file_operations fops =
{
.open = dev_open,
.read = dev_read,
.write = dev_write,
.release = dev_close,
};
static int __init chardev_init(void){
printk(KERN_INFO "SimpleChar: Initializing the SimpleChar\n");
// Dynamically allocate a major number
majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
if (majorNumber<0){
printk(KERN_ALERT "SimpleChar failed to register a major number\n");
return majorNumber;
}
printk(KERN_INFO "SimpleChar: registered correctly with major number %d\n", majorNumber);
// Register the device class
charClass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(charClass)){
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(charClass);
}
printk(KERN_INFO "SimpleChar: device class registered correctly\n");
// Register the device driver
if (IS_ERR(device_create(charClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME))){
class_destroy(charClass);
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return -1;
}
printk(KERN_INFO "SimpleChar: device class created correctly\n");
return 0;
}
static void __exit chardev_exit(void){
device_destroy(charClass, MKDEV(majorNumber, 0));
class_unregister(charClass);
class_destroy(charClass);
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_INFO "SimpleChar: Goodbye from the LKM!\n");
}
module_init(chardev_init);
module_exit(chardev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver");
MODULE_VERSION("0.1");
6. 加载驱动和创建设备文件
编译并加载上述模块到内核后,内核会为该设备分配一个主设备号。可以通过dmesg
命令查看分配的主设备号。一旦获得该设备号,我们就可以使用mknod
命令创建设备文件:
sudo mknod /dev/simple_char_dev c [majorNumber] 0
请将[majorNumber]
替换为实际分配给你的设备的主设备号。
7. 测试设备驱动功能
现在,你可以通过常规文件操作函数来测试设备驱动的功能了。例如,使用以下命令:
- 打开设备文件:
sudo cat /dev/simple_char_dev
- 写入设备文件:
echo "Hello, device!" | sudo tee /dev/simple_char_dev
- 读取设备文件:
sudo cat /dev/simple_char_dev
- 关闭设备文件:关闭文件操作是隐式执行的,由系统在
cat
命令完成后自动处理。
通过检查dmesg
命令的输出,你可以看到设备驱动在每次文件操作时打印的消息,这证明了驱动的open
, read
, write
, 和 close
方法被成功调用。
8. 结语
本文提供了一个关于如何开发、加载和测试Linux字符设备驱动的简单示例。虽然这个示例相对基础,但它为深入理解字符设备驱动的开发过程奠定了良好的基础。
标签:字符,struct,dev,char,内核,file,Linux,驱动,设备 From: https://blog.csdn.net/weixin_37787043/article/details/137449623