网站上怎样做超链接,制作短视频的软件有哪些,网页设计网站费用,wordpress的菜单静态linux 驱动开发1. 驱动程序的类型2. 驱动开发流程字符设备驱动 1. 基本概念2. 字符设备驱动的基本结构 架构字符设备驱动开发中常用的 API示例以下代码加入了设备类和设备实例的创建 linux 驱动开发
1. 驱动程序的类型
在 Linux 中#xff0c;驱动程序主要有以下几种类型驱动程序主要有以下几种类型
字符设备驱动处理字节流的设备如串口、键盘等。它们通过字符设备接口如 /dev/tty与用户空间进行交互。
块设备驱动处理块存储设备如硬盘、SSD 等。它们支持随机访问提供高效的数据传输。
网络设备驱动用于网络接口卡处理网络数据包的发送和接收。
USB 驱动支持 USB 设备的连接和管理。
2. 驱动开发流程
以下是开发 Linux 驱动的一般流程
2.1 环境准备
确保你有一个支持的 Linux 发行版。
安装开发工具如 gcc 和 make。
下载和编译 Linux 内核源代码与你的内核版本一致。2.2 编写驱动代码
根据你要开发的设备类型选择合适的接口。
创建相应的初始化init和退出exit函数。
根据设备实现文件操作结构体 file_operations。2.3 编写 Makefile
创建 Makefile用于编译你的模块。2.4 编译模块
在终端中通过 make 命令编译模块。2.5 加载和测试模块
使用 insmod 加载模块使用 rmmod 卸载模块。
通过 dmesg 查看内核日志调试和验证模块功能。
字符设备驱动
字符设备驱动是Linux内核中的一种设备驱动类型 主要用于处理字符设备如键盘、鼠标、串口等。 字符设备以字节流的形式进行数据传输与块设备如硬盘、SSD等不同 块设备以固定大小的块进行数据传输。
下面是一个简单的字符设备驱动入门指南
1. 基本概念
字符设备Character Device以字节流的形式进行数据传输的设备。
设备文件在Linux中字符设备通过设备文件通常位于/dev目录下与用户空间进行交互。
主设备号和次设备号设备文件由主设备号和次设备号标识。主设备号用于标识设备驱动次设备号用于标识特定的设备实例。
2. 字符设备驱动的基本结构
字符设备驱动通常包括以下几个部分
设备注册向内核注册字符设备。
文件操作接口定义设备文件的操作接口如open, read, write, close等。
设备操作函数实现具体的设备操作逻辑。
架构
字符设备驱动程序在 Linux 中的架构通常包括以下几个关键组件设备注册 使用 register_chrdev 注册设备并提供操作函数。驱动程序需要处理打开、读取、写入和关闭操作通常通过定义 file_operations 结构体来实现。打开、读写、关闭接口 驱动程序需要实现用户空间程序可以调用的接口。读取和写入操作使用 copy_to_user 和 copy_from_user 进行数据传输。内存管理 使用 kmalloc 和 kfree 进行动态内存分配和释放以管理驱动所需的数据结构。进程同步 使用等待队列基于 wait_queue_head_t来管理进程的等待与唤醒实现异步操作。错误处理 驱动程序需要实现适当的错误处理机制以确保在发生错误时能够提供适当的反馈。字符设备驱动开发中常用的 API
/** 1. register_chrdev* 功能注册字符设备。* 参数* int nr: 主设备号。如果设置为 0内核会自动分配一个主设备号。* const char *name: 设备的名字将显示在 /proc/devices 中。* struct file_operations *fops: 指向文件操作结构体的指针定义设备的操作函数。* 返回值* 返回主设备号如果发生错误则返回负值。*/
int register_chrdev(unsigned int nr, const char *name, struct file_operations *fops);/** 2. unregister_chrdev* 功能注销字符设备。* 参数* int nr: 要注销的主设备号。* const char *name: 设备的名字。* 返回值* 无。*/
void unregister_chrdev(unsigned int nr, const char *name);/** 3. copy_to_user* 功能将数据从内核空间复制到用户空间。* 参数* void __user *to: 用户空间的目标地址。* const void *from: 内核空间的源地址。* unsigned long count: 要复制的字节数。* 返回值* 返回未成功复制的字节数。如果为 0表示成功。*/
long copy_to_user(void __user *to, const void *from, unsigned long count);/** 4. copy_from_user* 功能将数据从用户空间复制到内核空间。* 参数* void *to: 内核空间的目标地址。* const void __user *from: 用户空间的源地址。* unsigned long count: 要复制的字节数。* 返回值* 返回未成功复制的字节数。如果为 0表示成功。*/
long copy_from_user(void *to, const void __user *from, unsigned long count);/** 5. kmalloc* 功能分配内核内存。* 参数* size_t size: 要分配的内存字节数。* gfp_t flags: 分配内存的标志如 GFP_KERNEL。* 返回值* 返回指向分配内存区域的指针。如果分配失败返回 NULL。*/
void *kmalloc(size_t size, gfp_t flags);/** 6. kfree* 功能释放内核内存。* 参数* void *ptr: 指向要释放内存的指针。* 返回值* 无。*/
void kfree(void *ptr);/** 7. init_waitqueue_head* 功能初始化等待队列头。* 参数* wait_queue_head_t *q: 等待队列头指针。* 返回值* 无。*/
void init_waitqueue_head(wait_queue_head_t *q);/** 8. wait_event* 功能将当前进程放入等待队列直到条件满足为止。* 参数* wait_queue_head_t *q: 等待队列头指针。* int condition: 条件表达式当其评估为真时返回进程。* 返回值* 无。*/
#define wait_event(q, condition) wait_event_interruptible(q, condition)/** 9. wake_up* 功能唤醒等待队列中的进程。* 参数* wait_queue_head_t *q: 等待队列头指针。* 返回值* 无。*/
void wake_up(wait_queue_head_t *q);/** 10. printk* 功能在内核中打印日志信息。* 参数* const char *fmt: 格式化字符串。* 返回值* 返回打印的字符数。*/
int printk(const char *fmt, ...);
示例
#include linux/module.h // 包含模块相关的宏和函数
#include linux/init.h // 包含模块初始化和退出相关的宏
#include linux/fs.h // 包含文件系统相关的函数和结构体
#include linux/cdev.h // 包含字符设备相关的函数和结构体
#include linux/uaccess.h // 包含用户空间和内核空间数据传输的函数// 设备号
static dev_t dev_num;// 字符设备结构体
static struct cdev chr_dev;// 设备缓冲区
static char buffer[1024];// 缓冲区大小
static int buffer_size 0;// 模块许可证
MODULE_LICENSE(GPL);// 模块作者
MODULE_AUTHOR(gopher);// 模块描述
MODULE_DESCRIPTION(A simple character device driver);// 打开设备文件时的回调函数
// 打开设备文件时内核会创建设备文件结构体和相关资源
// 该函数在内核空间中执行因此不能有进程上下文的操作
// 该函数的返回值是成功与否的标志
//param struct inode *inode 设备文件节点结构体
// param struct file *file 设备文件结构体
static int chr_dev_open(struct inode *inode, struct file *file) {printk(KERN_INFO chr_dev: Device opened\n);//打印设备号return 0;
}// 关闭设备文件时的回调函数
// 关闭设备文件时内核会释放设备文件结构体和相关资源
// 该函数在内核空间中执行因此不能有进程上下文的操作
// 该函数的返回值是成功与否的标志
//param struct inode *inode 设备文件节点结构体
// param struct file *file 设备文件结构体
static int chr_dev_release(struct inode *inode, struct file *file) {printk(KERN_INFO chr_dev: Device closed\n);return 0;
}// 从设备读取数据时的回调函数
// 从设备读取数据时内核会将数据从内核缓冲区复制到用户空间缓冲区
// 并更新读取位置
// 从设备读取数据时内核会返回实际读取的字节数
// 如果读取位置超出缓冲区大小则返回0
// 如果发生其他错误则返回-EFAULT
// 成功读取数据时返回实际读取的字节数
// 注意从设备读取数据时内核不会阻塞而是立即返回
//param struct file *file 设备文件结构体
// param char __user *user_buf 用户空间缓冲区
// param size_t count 要读取的字节数
// param loff_t *ppos 读取位置指针
static ssize_t chr_dev_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) {int ret;// 检查读取位置是否超出缓冲区大小if (*ppos buffer_size)return 0;// 将数据从内核缓冲区复制到用户空间缓冲区ret copy_to_user(user_buf, buffer *ppos, count);if (ret)return -EFAULT; // 复制失败printk(KERN_INFO chr_dev: Read %d bytes from device\n, count);// 更新读取位置*ppos count;return count;
}// 向设备写入数据时的回调函数
// 向设备写入数据时内核会将数据从用户空间缓冲区复制到内核缓冲区
// 并更新缓冲区大小
// 向设备写入数据时内核会返回实际写入的字节数
// 如果缓冲区已满则返回-ENOSPC
// 如果发生其他错误则返回-EFAULT
// 成功写入数据时返回实际写入的字节数
// 注意向设备写入数据时内核不会阻塞而是立即返回
//param struct file *file 设备文件结构体
// param const char __user *user_buf 用户空间缓冲区
// param size_t count 要写入的字节数
// param loff_t *ppos 读取位置指针
static ssize_t chr_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) {int ret;// 检查缓冲区是否有足够的空间if (count sizeof(buffer) - buffer_size)return -ENOSPC; // 空间不足// 将数据从用户空间缓冲区复制到内核缓冲区//param void *to 目的地址// param const void *from 源地址// param size_t n 字节数ret copy_from_user(buffer buffer_size, user_buf, count);if (ret)return -EFAULT; // 复制失败// 打印调试信息printk(KERN_INFO chr_dev: Wrote %d bytes to device\n, count);// 更新缓冲区大小buffer_size count;return count;
}// 文件操作结构体定义了设备文件的操作接口
// 包含了文件操作的回调函数和一些其他属性
// 这些回调函数在设备文件被打开、关闭、读、写时被调用
static struct file_operations chr_dev_fops {.owner THIS_MODULE, // 模块所有者.open chr_dev_open, // 打开设备文件的回调函数.release chr_dev_release, // 关闭设备文件的回调函数.read chr_dev_read, // 从设备读取数据的回调函数.write chr_dev_write, // 向设备写入数据的回调函数
};// 模块初始化函数
static int __init chr_dev_init(void) {int ret;// 分配设备号//param dev_t dev_num 设备号// param unsigned int major 主设备号// param unsigned int minor 次设备号// param const char *name 设备名ret alloc_chrdev_region(dev_num, 0, 1, chr_dev);if (ret 0) {printk(KERN_ERR chr_dev: Failed to allocate device number\n);return ret;}// 初始化字符设备//param struct cdev *cdev 字符设备结构体// param struct file_operations *fops 字符设备操作结构体cdev_init(chr_dev, chr_dev_fops);chr_dev.owner THIS_MODULE;// 添加字符设备//param struct cdev *cdev 字符设备结构体// param dev_t dev 设备号// param unsigned int count 设备号计数ret cdev_add(chr_dev, dev_num, 1);if (ret 0) {printk(KERN_ERR chr_dev: Failed to add character device\n);unregister_chrdev_region(dev_num, 1);return ret;}printk(KERN_INFO chr_dev: Device initialized\n);//打印设备号printk(KERN_INFO chr_dev: Major number: %d\n, MAJOR(dev_num));printk(KERN_INFO chr_dev: Minor number: %d\n, MINOR(dev_num));return 0;
}// 模块退出函数
static void __exit chr_dev_exit(void) {// 删除字符设备//param dev_t dev 设备号/cdev_del(chr_dev);// 释放设备号//param dev_t dev 设备号 // param unsigned int count 设备号计数unregister_chrdev_region(dev_num, 1);(KERN_INFO chr_dev: Device removed\n);
}// 注册模块初始化函数
// 内核在加载模块时调用模块的初始化函数
// 该函数在内核空间中执行因此不能有进程上下文的操作
// 该函数的返回值是模块初始化成功与否的标志
module_init(chr_dev_init);// 注册模块退出函数
// 内核在卸载模块时调用模块的退出函数
// 该函数在内核空间中执行因此不能有进程上下文的操作
module_exit(chr_dev_exit);以下代码加入了设备类和设备实例的创建 /*以下代码加入了设备类和设备实例的创建*/
#include linux/module.h // 包含模块相关的宏和函数
#include linux/init.h // 包含模块初始化和退出相关的宏
#include linux/fs.h // 包含文件系统相关的函数和结构体
#include linux/cdev.h // 包含字符设备相关的函数和结构体
#include linux/uaccess.h // 包含用户空间和内核空间数据传输的函数
#include linux/device.h // 包含设备类和设备实例相关的函数// 设备号
static dev_t dev_num;// 字符设备结构体
static struct cdev chr_dev;// 设备缓冲区
static char buffer[1024];// 缓冲区大小
static int buffer_size 0;// 设备类
static struct class *chr_dev_class;// 设备实例
static struct device *chr_dev_device;// 模块许可证
MODULE_LICENSE(GPL);// 模块作者
MODULE_AUTHOR(gopher);// 模块描述
MODULE_DESCRIPTION(A simple character device driver);// 打开设备文件时的回调函数
static int chr_dev_open(struct inode *inode, struct file *file) {printk(KERN_INFO chr_dev: Device opened\n);return 0;
}// 关闭设备文件时的回调函数
static int chr_dev_release(struct inode *inode, struct file *file) {printk(KERN_INFO chr_dev: Device closed\n);return 0;
}// 从设备读取数据时的回调函数
static ssize_t chr_dev_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) {int ret;if (*ppos buffer_size)return 0;ret copy_to_user(user_buf, buffer *ppos, count);if (ret)return -EFAULT;printk(KERN_INFO chr_dev: Read %zu bytes from device\n, count);*ppos count;return count;
}// 向设备写入数据时的回调函数
static ssize_t chr_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) {int ret;if (count sizeof(buffer) - buffer_size)return -ENOSPC;ret copy_from_user(buffer buffer_size, user_buf, count);if (ret)return -EFAULT;printk(KERN_INFO chr_dev: Wrote %zu bytes to device\n, count);buffer_size count;return count;
}// 文件操作结构体
static struct file_operations chr_dev_fops {.owner THIS_MODULE,.open chr_dev_open,.release chr_dev_release,.read chr_dev_read,.write chr_dev_write,
};// 模块初始化函数
static int __init chr_dev_init(void) {int ret;// 分配设备号ret alloc_chrdev_region(dev_num, 0, 1, chr_dev);if (ret 0) {printk(KERN_ERR chr_dev: Failed to allocate device number\n);return ret;}// 初始化字符设备cdev_init(chr_dev, chr_dev_fops);chr_dev.owner THIS_MODULE;// 添加字符设备ret cdev_add(chr_dev, dev_num, 1);if (ret 0) {printk(KERN_ERR chr_dev: Failed to add character device\n);unregister_chrdev_region(dev_num, 1);return ret;}// 创建设备类// 用于管理设备实例// param struct module *owner 模块所有者// param const char *name 设备类名// return struct class * 设备类结构体chr_dev_class class_create(THIS_MODULE, chr_dev_class);if (IS_ERR(chr_dev_class)) {// 出错printk(KERN_ERR chr_dev: Failed to create device class\n);cdev_del(chr_dev);// 删除字符设备unregister_chrdev_region(dev_num, 1);// 释放设备号return PTR_ERR(chr_dev_class);// 返回错误码}// 创建设备实例// 用于管理设备文件// param struct class *class 设备类结构体// param struct device *parent 父设备实例// param dev_t dev 设备号// param void *drvdata 设备数据// param const char *fmt 设备名格式// return struct device * 设备实例结构体chr_dev_device device_create(chr_dev_class, NULL, dev_num, NULL, chr_dev);if (IS_ERR(chr_dev_device)) {printk(KERN_ERR chr_dev: Failed to create device\n);class_destroy(chr_dev_class);// 删除设备类cdev_del(chr_dev);// 删除字符设备unregister_chrdev_region(dev_num, 1);// 释放设备号return PTR_ERR(chr_dev_device);// 返回错误码}printk(KERN_INFO chr_dev: Device initialized\n);// 打印调试信息printk(KERN_INFO chr_dev: Major number: %d\n, MAJOR(dev_num));// 打印设备号printk(KERN_INFO chr_dev: Minor number: %d\n, MINOR(dev_num));// 打印设备号return 0;
}// 模块退出函数
static void __exit chr_dev_exit(void) {// 删除设备实例device_destroy(chr_dev_class, dev_num);// 删除设备类class_destroy(chr_dev_class);// 删除字符设备cdev_del(chr_dev);// 释放设备号unregister_chrdev_region(dev_num, 1);printk(KERN_INFO chr_dev: Device removed\n);
}// 注册模块初始化函数
module_init(chr_dev_init);// 注册模块退出函数
module_exit(chr_dev_exit);