内核开发学习-杂项设备miscdevice

本文介绍了如何在Linux系统中编写一个使用杂项接口创建的字符设备驱动程序,涉及动态次设备号管理、read和write函数实现、设备节点定位以及模块的注册和注销。通过实例代码展示了如何完成设备注册、读写操作和错误处理过程。
摘要由CSDN通过智能技术生成

任务

写一个杂项字符设备驱动程序。杂项接口是创建字符设备的一种非常简单的方式,不需要担心所有的sysfs和字符设备注册混乱,因此尽可能地使用简单的接口。

  • 杂项设备应该使用动态的次设备号创建,不需要去尝试预留一个真实的次设备号来用于你的测试模块,那样做会很疯狂。

  • 杂项设备应该实现读和写功能。

  • 杂项设备节点应该在/dev/eudyptula中显示。

  • 当从字符设备节点读取时,你的分配的ID应该返回给调用者。

  • 当向字符设备节点写入时,发送到内核的数据需要被检查。如果它匹配你的分配ID,则返回正确的写入返回值。如果值不匹配你的分配ID,返回“无效值”错误值。

  • 当你的模块加载时,杂项设备应该被注册,当它卸载时应该被注销。

思路

1. 创建带有动态次设备号的杂项设备:
  - 使用Linux内核提供的misc_register函数注册一个杂项设备。struct miscdevice结构体中的minor成员应设置为MISC_DYNAMIC_MINOR,以使用动态分配的次设备号。
2. 实现读写函数:
  - 定义read和write函数,并在struct file_operations结构体中引用它们。这些函数将处理对设备文件的读写操作。
  - read函数应返回分配给您的ID。
  - write函数应检查写入的数据是否与您的分配ID匹配。如果匹配,返回正确的写入字节数;如果不匹配,返回错误码(例如,-EINVAL)。
3. 设备节点应出现在/dev/eudyptula:
  - 在struct miscdevice结构体中设置name字段为"eudyptula"。这将导致创建名为/dev/eudyptula的设备文件。
4. 模块加载与卸载时的注册和注销:
  - 在模块的初始化函数中调用misc_register来注册杂项设备。
  - 在模块的退出函数中调用misc_deregister来注销杂项设备。
5. 提供证明:
  - 编写测试脚本或程序,以演示设备文件的创建、读写操作的正确性和错误处理。
  - 使用dmesg日志来显示模块加载和卸载的消息,以及读写操作的日志输出。

代码

helloworld.c如下

#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/errno.h>

#define MY_ID "7c1caf2f50d1"
#define MY_ID_LEN 13	/* ID length including the final NULL */

static ssize_t hello_read(struct file *file, char __user *buf,
			size_t count, loff_t *ppos)
{
	char *hello_str = MY_ID;

	if (*ppos != 0)
		return 0;

	if ((count < MY_ID_LEN) ||
		(copy_to_user(buf, hello_str, MY_ID_LEN)))
		return -EINVAL;

	*ppos += count;
	return count;
}

static ssize_t hello_write(struct file *file, char const __user *buf,
			size_t count, loff_t *ppos)
{
	char *hello_str = MY_ID;
	char input[MY_ID_LEN];

	if ((count != MY_ID_LEN) ||
		(copy_from_user(input, buf, MY_ID_LEN)) ||
		(strncmp(hello_str, input, MY_ID_LEN - 1)))
		return -EINVAL;
	else
		return count;
}

static const struct file_operations hello_fops = {
	.owner = THIS_MODULE,
	.read = hello_read,
	.write = hello_write
};

static struct miscdevice hello_dev = {
	MISC_DYNAMIC_MINOR,
	"eudyptula",
	&hello_fops
};

static int __init hello_init(void)
{
	int ret;

	ret = misc_register(&hello_dev);
	pr_debug("Hello World!\n");

	return ret;
}

static void __exit hello_exit(void)
{
	misc_deregister(&hello_dev);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("7c1caf2f50d1");
MODULE_DESCRIPTION("Misc char hello world module");

makefile如下:

CFLAGS_helloworld.o = -DDEBUG

obj-m += helloworld.o

KDIR ?= /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

default:
	$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

测试

执行如下命令,编译并插入内核模块

make clean
make
sudo insmod helloworld.ko
lsmod | head

模块成功加载,如图:

查看设备节点

测试设备的读写功能

尝试用脚本测试设备的读写功能

read.sh如下,并添加执行权限

#!/bin/bash

READ_DATA=$(sudo cat /dev/eudyptula)

echo "read result: $READ_DATA"

write.sh如下:

#!/bin/bash

DATA="7c1caf2f50d1"

echo "$DATA" | sudo tee /dev/eudyptula

if [ $? -eq 0 ]; then
    echo "write success"
else
    echo "write failed"
fi

最后记得rmmod模块

问题解决

1.为什么只有读的权限没有写的权限,加了sudo也不行

使用sudo echo "test" > /dev/eudyptula命令时,遇到权限问题的原因在于重定向操作(>)是由当前的shell执行的,而不是sudo。即使echo命令有超级用户权限,写入/dev/eudyptula的操作却是以普通用户的权限执行的。为了解决这个问题,可以使用以下几种方法之一:

使用tee命令:(亲测有效)

echo "test" | sudo tee /dev/eudyptula

这里,echo命令的输出被传递给tee,tee以超级用户权限写入到/dev/eudyptula。

2.如果make出错,需要重新编译内核,保证makefile的lib/modules/下存在与uname -r一致的编译目录

说明

本项目来源于github开源项目eudyptula

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值