markdown复制## 1. Linux内核模块化开发基础
### 1.1 内核模块的结构与编译
Linux内核模块是可以在运行时动态加载到内核中的代码片段,它们扩展了内核功能而无需重新编译整个内核。典型的模块包含以下基本结构:
```c
#include <linux/module.h>
#include <linux/init.h>
static int __init mymodule_init(void)
{
printk(KERN_INFO "Module loaded\n");
return 0;
}
static void __exit mymodule_exit(void)
{
printk(KERN_INFO "Module unloaded\n");
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
编译内核模块需要特殊的Makefile:
makefile复制obj-m := mymodule.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
关键点说明:
module_init和module_exit宏定义了模块的加载和卸载函数MODULE_LICENSE必须声明,GPL是最常用的许可证- 内核模块使用printk而非printf进行日志输出
- 编译时需要指向当前运行内核的源码目录
1.2 模块参数传递
内核模块支持运行时参数传递,这是通过module_param宏实现的:
c复制static int debug_level = 1;
module_param(debug_level, int, S_IRUGO|S_IWUSR);
static char *device_name = "default";
module_param(device_name, charp, S_IRUGO);
参数可以通过两种方式传递:
- insmod时直接指定:
insmod mymodule.ko debug_level=3 device_name=test - 在
/sys/module/mymodule/parameters/目录下修改(需有写权限)
权限位说明:
S_IRUGO:用户、组和其他用户可读S_IWUSR:仅root用户可写
1.3 符号导出与模块间通信
模块间可以通过符号导出实现函数和变量的共享:
c复制// 在模块A中导出符号
void shared_function(void) { /*...*/ }
EXPORT_SYMBOL(shared_function);
// 在模块B中使用
extern void shared_function(void);
注意事项:
- 导出的符号会出现在
/proc/kallsyms中 - 模块卸载时应确保没有其他模块在使用导出的符号
- 使用
EXPORT_SYMBOL_GPL限制只有GPL兼容模块可以使用该符号
2. 字符设备驱动开发
2.1 设备号管理
Linux设备分为三种类型:
- 字符设备(c):按字节流访问,如串口、键盘
- 块设备(b):按块访问,如硬盘
- 网络设备:通过socket接口访问
设备号由主设备号和次设备号组成:
c复制dev_t dev = MKDEV(250, 0); // 主设备号250,次设备号0
设备号申请方式:
c复制// 静态申请
int register_chrdev_region(dev_t first, unsigned int count, const char *name);
// 动态申请
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,
unsigned int count, const char *name);
最佳实践:
- 主设备号标识设备类型,次设备号标识具体实例
- 优先使用动态分配以避免冲突
- 设备号资源通过
/proc/devices查看
2.2 字符设备注册
完整的字符设备注册流程:
c复制static struct file_operations fops = {
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
.unlocked_ioctl = my_ioctl,
};
static int __init my_init(void)
{
alloc_chrdev_region(&dev, 0, 1, "mydev");
cdev_init(&cdev, &fops);
cdev_add(&cdev, dev, 1);
// 创建设备节点
class_create(THIS_MODULE, "myclass");
device_create(myclass, NULL, dev, NULL, "mydev");
return 0;
}
关键数据结构:
struct file_operations:定义设备操作接口struct cdev:内核内部表示字符设备的结构struct class:用于sysfs中的设备分类
2.3 读写操作实现
典型的read/write实现需要考虑:
- 用户空间与内核空间的数据交换(copy_to_user/copy_from_user)
- 并发控制(使用互斥锁或自旋锁)
- 阻塞与非阻塞I/O处理
c复制static ssize_t my_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
struct my_device *dev = filp->private_data;
int ret;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
ret = copy_to_user(buf, dev->buffer, min(count, dev->size));
if (ret) {
ret = -EFAULT;
goto out;
}
*f_pos += count - ret;
ret = count - ret;
out:
mutex_unlock(&dev->lock);
return ret;
}
并发处理要点:
- 长时间操作使用互斥锁(mutex)
- 短时间操作使用自旋锁(spinlock)
- 注意锁的获取顺序以避免死锁
- 考虑信号中断处理(interruptible版本)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
3. 平台设备驱动模型
3.1 Platform设备与驱动
Platform总线用于片上系统(SoC)中的设备管理,分为两部分:
- Platform设备(描述硬件资源):
c复制static struct resource mydev_res[] = {
[0] = DEFINE_RES_MEM(0x12340000, 0x100),
[1] = DEFINE_RES_IRQ(IRQ_NUM),
};
static struct platform_device mydev = {
.name = "my-platform-device",
.id = -1,
.num_resources = ARRAY_SIZE(mydev_res),
.resource = mydev_res,
};
- Platform驱动(实现设备操作):
c复制static int my_probe(struct platform_device *pdev)
{
struct resource *res;
void __iomem *regs;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res);
