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
相关推荐
陈希1237 分钟前
Linux 服务器性能排查手册(2026):CPU、内存、磁盘IO、网络四大瓶颈定位
linux·服务器·网络
测试界的小码农3 小时前
【Linux 命令实战(03):文件创建、复制、移动与删除,掌握 touch、mkdir、cp、mv、rm】
linux·服务器·php
byte轻骑兵4 小时前
实战 1:修改 BlueZ 源码,添加自定义开机日志
linux·aiot·bluez·电脑蓝牙·嵌入式蓝牙
叼烟扛炮4 小时前
Linux 学习笔记(五十):网络层——IP 协议、子网划分与 NAT 魔法
linux·笔记·学习
叼烟扛炮4 小时前
Linux 学习笔记(六十):单线程的逆袭——epoll 高并发服务器实战
linux·服务器·网络·笔记·学习·算法
AR-26710-5 小时前
Linux Day32——实操五类资源告急排查
linux·运维
StarsGreen5 小时前
libva中的图像格式说明
linux
H.莓飛6 小时前
【C++】【STL】vector_OJ题
linux·服务器·开发语言·数据结构·c++·visual studio
Rabitebla6 小时前
【Linux操作系统】进程篇(十一):进程程序替换——从底层原理到 exec 函数族
linux·服务器·c++·笔记·学习·算法
无人生还别怕7 小时前
如何搭建docker仓库(轻量级)registry
linux·运维·docker·云原生·容器·registry·openanolisos