Linux内核模块开发完整教程:从Hello World到设备驱动

Linux内核模块开发完整教程:从Hello World到设备驱动

本文提供完整的Linux内核模块开发指南,帮助开发者从基础的Hello World模块到复杂的设备驱动程序。

1. 内核模块基础概念

Linux内核模块是可以在运行时动态加载和卸载的代码片段。它们扩展了内核的功能而无需重新编译整个内核。

2. 环境准备

首先安装必要的开发工具:

# Ubuntu/Debian
sudo apt-get install build-essential linux-headers-$(uname -r)

# CentOS/RHEL
sudo yum install gcc make kernel-devel kernel-headers

3. Hello World模块

创建最简单的内核模块:

// hello.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World module");
MODULE_VERSION("0.1");

static int __init hello_init(void) {
    printk(KERN_INFO "Hello, World!\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Goodbye, World!\n");
}

module_init(hello_init);
module_exit(hello_exit);

4. Makefile编写

# Makefile
obj-m += hello.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

5. 编译和加载模块

# 编译模块
make

# 加载模块
sudo insmod hello.ko

# 查看内核日志
dmesg | tail

# 卸载模块
sudo rmmod hello

6. 参数传递

向模块传递参数:

#include <linux/moduleparam.h>

static char *name = "World";
static int times = 1;

module_param(name, charp, S_IRUGO);
module_param(times, int, S_IRUGO);

static int __init hello_init(void) {
    int i;
    for (i = 0; i < times; i++) {
        printk(KERN_INFO "Hello, %s!\n", name);
    }
    return 0;
}

7. 设备驱动框架

字符设备驱动示例:

#include <linux/fs.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "mydevice"
#define BUFFER_SIZE 1024

static int major_number;
static char buffer[BUFFER_SIZE];
static int buffer_ptr = 0;

static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);

static struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .open = device_open,
    .release = device_release
};

static int __init init_module(void) {
    major_number = register_chrdev(0, DEVICE_NAME, &fops);
    if (major_number < 0) {
        printk(KERN_ALERT "Failed to register major number\n");
        return major_number;
    }
    printk(KERN_INFO "Registered major number %d\n", major_number);
    return 0;
}

static void __exit cleanup_module(void) {
    unregister_chrdev(major_number, DEVICE_NAME);
    printk(KERN_INFO "Unregistered major number %d\n", major_number);
}

8. 内存管理

内核内存分配:

// 分配内存
void *ptr = kmalloc(size, GFP_KERNEL);

// 释放内存
kfree(ptr);

// 页面分配
unsigned long page = __get_free_page(GFP_KERNEL);

// 释放页面
free_page(page);

9. 同步机制

使用自旋锁:

#include <linux/spinlock.h>

static DEFINE_SPINLOCK(my_lock);

// 获取锁
spin_lock(&my_lock);
// 临界区代码
spin_unlock(&my_lock);

10. 调试技巧

  • 使用printk进行调试输出
  • 检查/proc文件系统
  • 使用kgdb进行内核调试
  • 启用内核配置选项DEBUG_KERNEL

总结

Linux内核模块开发是一个复杂但强大的技术。通过本文的指导,您可以从简单的Hello World模块开始,逐步掌握设备驱动开发的核心技能。记住始终在虚拟机或测试环境中进行开发,避免损坏生产系统。

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容