完整教程:Linux 驱动(.ko)基本框架

Linux 驱动(.ko)基本框架

1. 核心组件
  • 源代码.c 文件(驱动实现)
  • Makefile:指导编译过程
  • Kbuild 系统:Linux 内核的构建系统
2. 编译流程
驱动源码 .c
Makefile
内核头文件
内核构建系统 Kbuild
预处理/编译/汇编
目标文件 .o
链接为 .ko
3. Makefile 关键要素
# 指定内核源码路径(必须)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# 当前模块目录
PWD := $(shell pwd)
# 模块名称(生成 my_module.ko)
obj-m := my_module.o
# 多文件驱动
my_module-objs := file1.o file2.o
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean

驱动代码基本框架(字符设备示例)

1. 最小驱动结构
#include <linux/module.h>
  #include <linux/fs.h>
    #include <linux/cdev.h>
      #define DEV_NAME "my_dev"
      static int major = 0;
      static struct cdev my_cdev;
      static int my_open(struct inode *inode, struct file *file) {
      printk(KERN_INFO "Device opened\n");
      return 0;
      }
      static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *fpos) {
      const char *msg = "Hello from kernel!\n";
      size_t len = strlen(msg);
      if (copy_to_user(buf, msg, min(len, count)))
      return -EFAULT;
      return min(len, count);
      }
      static struct file_operations fops = {
      .owner = THIS_MODULE,
      .open = my_open,
      .read = my_read,
      };
      static int __init my_init(void) {
      // 1. 分配设备号
      alloc_chrdev_region(&major, 0, 1, DEV_NAME);
      // 2. 初始化cdev
      cdev_init(&my_cdev, &fops);
      my_cdev.owner = THIS_MODULE;
      // 3. 添加到系统
      cdev_add(&my_cdev, MKDEV(major, 0), 1);
      printk(KERN_INFO "Driver loaded, major=%d\n", major);
      return 0;
      }
      static void __exit my_exit(void) {
      cdev_del(&my_cdev);
      unregister_chrdev_region(MKDEV(major, 0), 1);
      printk(KERN_INFO "Driver unloaded\n");
      }
      module_init(my_init);
      module_exit(my_exit);
      MODULE_LICENSE("GPL");
      MODULE_AUTHOR("Your Name");
      MODULE_DESCRIPTION("Simple Character Driver");
2. 关键组件说明
  1. 初始化和退出函数

    • module_init():驱动加载时调用
    • module_exit():驱动卸载时调用
  2. 设备操作集

    struct file_operations {
    .owner = THIS_MODULE,
    .open = my_open,
    .read = my_read,
    .write = my_write,
    .release = my_release,
    // ... 其他操作
    };
  3. 字符设备注册

    • alloc_chrdev_region():动态申请设备号
    • cdev_init():初始化cdev结构体
    • cdev_add():添加设备到系统
  4. 用户-内核空间交互

    • copy_to_user():内核→用户空间
    • copy_from_user():用户→内核空间

编译与测试流程

  1. 编译驱动
    make # 生成 my_module.ko
  2. 加载驱动
    sudo insmod my_module.ko
    dmesg | tail # 查看加载日志
  3. 创建设备节点
    sudo mknod /dev/my_dev c 250 0 # 250替换为dmesg中的major号
  4. 测试驱动
    cat /dev/my_dev # 应输出 "Hello from kernel!"
  5. 卸载驱动
    sudo rmmod my_module

关键注意事项

  1. 内核API兼容性

    • 使用 #include <linux/...> 而非标准C头文件
    • 遵循内核编码规范(如函数/变量命名)
  2. 并发控制

    • 使用互斥锁(mutex)或自旋锁(spinlock
  3. 错误处理

    • 所有内核函数需检查返回值
    • 确保退出时释放所有资源
  4. 调试

    • printk 分级:KERN_DEBUGKERN_ERR
    • /proc/kallsyms 查看符号表
    • strace 跟踪系统调用

完整开发建议:参考 Linux 内核文档 Documentation/driver-api/

posted @ 2025-08-09 11:27  yjbjingcha  阅读(84)  评论(0)    收藏  举报