Linux字符设备驱动的实现与QEMU验证

Linux字符设备驱动实现与验证

本文总结 simple_demo 这个最小 Linux 字符设备驱动 demo 的功能,并给出完整代码与最小编译、加载、验证方法。

假定内核已经提前编译完成,且可直接使用:

KDIR=~/workspace/Linux/linux

1. Demo 功能概述

simple_demo 是一个基于 miscdevice 的最小字符设备驱动。

加载模块后,内核会创建设备节点:

sh 复制代码
/dev/simple_demo

这个 demo 支持最基本的两类操作:

  • read():读取驱动内部保存的字符串
  • write():把用户写入的字符串保存到驱动内部缓冲区

默认行为

  • 模块加载后,默认内容为:
text 复制代码
hello from simple_demo
  • 用户向 /dev/simple_demo 写入新内容后,后续再读时就会返回最新写入的数据

2. 代码实现思路

这个 demo 的实现非常简单,核心只有四部分:

  • 一个全局缓冲区:保存当前字符串内容
  • 一个 read():把内核缓冲区内容拷给用户态
  • 一个 write():把用户态内容写入内核缓冲区

设备注册方式使用 miscdevice,这样不需要自己手动分配主设备号。

3. 完整代码

C 复制代码
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>

#define SIMPLE_DEMO_MAX_MSG_LEN 256
#define SIMPLE_DEMO_DEFAULT_MSG "hello from simple_demo\n"

static DEFINE_MUTEX(simple_demo_lock);
static char simple_demo_msg[SIMPLE_DEMO_MAX_MSG_LEN] = SIMPLE_DEMO_DEFAULT_MSG;
static size_t simple_demo_msg_len = sizeof(SIMPLE_DEMO_DEFAULT_MSG) - 1;

static ssize_t simple_demo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	ssize_t ret;

	mutex_lock(&simple_demo_lock);
	ret = simple_read_from_buffer(buf, count, ppos, simple_demo_msg, simple_demo_msg_len);
	mutex_unlock(&simple_demo_lock);

	return ret;
}

static ssize_t simple_demo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	size_t copy_len;

	if (!count)
		return 0;

	copy_len = min(count, (size_t)(SIMPLE_DEMO_MAX_MSG_LEN - 1));

	mutex_lock(&simple_demo_lock);
	if (copy_from_user(simple_demo_msg, buf, copy_len)) {
		mutex_unlock(&simple_demo_lock);
		return -EFAULT;
	}
	simple_demo_msg[copy_len] = '\0';
	simple_demo_msg_len = copy_len;
	mutex_unlock(&simple_demo_lock);

	return count;
}

static const struct file_operations simple_demo_fops = {
	.owner = THIS_MODULE,
	.read = simple_demo_read,
	.write = simple_demo_write,
	.llseek = noop_llseek,
};

static struct miscdevice simple_demo_miscdev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "simple_demo",
	.fops = &simple_demo_fops,
	.mode = 0666,
};

static int __init simple_demo_init(void)
{
	int ret;

	ret = misc_register(&simple_demo_miscdev);
	if (ret)
		return ret;

	pr_info("simple_demo: loaded (/dev/%s)\n", simple_demo_miscdev.name);
	return 0;
}

static void __exit simple_demo_exit(void)
{
	misc_deregister(&simple_demo_miscdev);
	pr_info("simple_demo: unloaded\n");
}

module_init(simple_demo_init);
module_exit(simple_demo_exit);

MODULE_AUTHOR("demo");
MODULE_DESCRIPTION("Simple misc char device driver demo");
MODULE_LICENSE("GPL");

4. 编译方法

假定内核已经编译好,内核代码位于~/workspace/Linux/linux,直接编这个驱动模块即可。

进入驱动Demo目录:

bash 复制代码
cd ~/workspace/Lab/drivers

编译命令:

bash 复制代码
make clean KDIR=~/workspace/Linux/linux
make KDIR=~/workspace/Linux/linux

编译成功后会生成:

text 复制代码
simple_demo.ko

可以再检查一下模块版本信息:

bash 复制代码
modinfo -F vermagic simple_demo.ko

输出中应包含:

text 复制代码
7.1.0-rc2+

输出必须等于 QEMU (我们用QEMU启动的内核)里的 uname -r

5. 加载与验证

宿主机与qemu之间传递文件可以参考《使用额外ext4磁盘镜像在QEMU中传递与加载内核模块》。

假设 simple_demo.ko 已经放到 QEMU 可访问目录 /mnt/host,在 QEMU 中执行:

sh 复制代码
insmod /mnt/host/simple_demo.ko
ls -l /dev/simple_demo

读取默认内容:

sh 复制代码
cat /dev/simple_demo

写入新内容并再次读取:

sh 复制代码
echo -n "from_user\n" > /dev/simple_demo
cat /dev/simple_demo

查看日志:

sh 复制代码
dmesg | tail -n 20

卸载模块:

sh 复制代码
rmmod simple_demo

6. 上层应用如何使用

上层应用把它当成普通字符设备文件即可,直接对 /dev/simple_demo 做文件操作:

  • open("/dev/simple_demo", O_RDWR)
  • read()
  • write()
  • close()

最简单的 shell 使用方式:

sh 复制代码
cat /dev/simple_demo
echo -n "hello driver\n" > /dev/simple_demo
cat /dev/simple_demo
相关推荐
jsons12 小时前
autofs挂载
linux·服务器·网络
liwulin05062 小时前
【ollama】自定义结构化输出
linux·前端·数据库·ollama
时空无限3 小时前
vllm 大模型启动缓存相关环境变量 export
linux·缓存·vllm
Echo_cy_3 小时前
基于ZYNQ-7000的Ethernet驱动配置
linux·驱动开发·嵌入式硬件·fpga开发·ethernet·zynq
ShineWinsu3 小时前
对于Linux:模版方法类的解析以及socket、TcpSocket的封装
linux·c++·面试·socket·模板方法模式·封装·tcpsocket
无足鸟ICT4 小时前
【RHCA+】查看变量
linux·运维·服务器
Kina_C4 小时前
Linux DNS 服务器-从高速缓存到辅助 DNS 部署指南
linux·运维·服务器·dns
suaizai_4 小时前
TypeScript 7 正式发布!Vue 暂被 “拒之门外“ !!!
linux·运维·ubuntu
慕伏白5 小时前
【慕伏白】Linux 系统如何根据端口号关闭进程
linux·运维·服务器
letisgo56 小时前
VMware Workstation + Ubuntu 26.04 LTS 小白运维部署手册
linux·运维·ubuntu