此方法应该不止适用于银河麒麟,但是本人只实验了银河麒麟V10-SP1。
测试代码
hello_world.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h> // 用于 class_create() 和 device_create()
#define DEVICE_NAME "hello_world"
#define CLASS_NAME "hello_class"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World Linux driver with udev support");
static int major;
static char message[] = "Hello from kernel!\n";
static int message_len = sizeof(message);
static struct class* hello_class = NULL;
static struct device* hello_device = NULL;
// 设备文件 open
static int dev_open(struct inode *inodep, struct file *filep) {
printk(KERN_INFO "Device opened\n");
return 0;
}
// 设备文件 read
static ssize_t dev_read(struct file *filep, char __user *buffer, size_t len, loff_t *offset) {
int bytes_read;
if (*offset >= message_len)
return 0;
bytes_read = message_len - *offset;
if (len < bytes_read)
bytes_read = len;
if (copy_to_user(buffer, message + *offset, bytes_read))
return -EFAULT;
*offset += bytes_read;
return bytes_read;
}
// 设备文件 release
static int dev_release(struct inode *inodep, struct file *filep) {
printk(KERN_INFO "Device closed\n");
return 0;
}
// 定义 file_operations 结构体
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = dev_open,
.read = dev_read,
.release = dev_release,
};
// 模块初始化
static int __init hello_world_init(void) {
major = register_chrdev(0, DEVICE_NAME, &fops);
if (major < 0) {
printk(KERN_ALERT "Failed to register a major number\n");
return major;
}
printk(KERN_INFO "Hello, World! Device registered with major number %d\n", major);
// 创建 class
hello_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(hello_class)) {
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(hello_class);
}
printk(KERN_INFO "Device class created successfully\n");
// 创建 device
hello_device = device_create(hello_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);
if (IS_ERR(hello_device)) {
class_destroy(hello_class);
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(hello_device);
}
printk(KERN_INFO "Device created successfully\n");
return 0;
}
// 模块卸载
static void __exit hello_world_exit(void) {
device_destroy(hello_class, MKDEV(major, 0));
class_destroy(hello_class);
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_INFO "Goodbye, World! Device unregistered\n");
}
module_init(hello_world_init);
module_exit(hello_world_exit);
Makefile
obj-m += hello_world.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
开机自动加载
- 将驱动模块复制到
/lib/modules/$(uname -r)/extra/
sudo mkdir -p /lib/modules/$(uname -r)/extra/
sudo cp hello_world.ko /lib/modules/$(uname -r)/extra/
- 在
/etc/modules
添加驱动名
echo "hello_world" | sudo tee -a /etc/modules
- 刷新模块依赖
sudo depmod -a
- 重启测试
sudo reboot
- 验证模块是否自动加载
lsmod | grep hello_world
通过上诉步骤应该能够实现开机自动加载,亲测有效,但是此时还有一个问题就是默认情况下,udev 创建的设备节点的 user 和 group 都是 root,普通用户无法访问。
所以使用以下命令实现权限修改
解决方案:修改 udev
规则
我们可以在 udev
规则中指定设备的 用户 (OWNER
)、用户组 (GROUP
) 和权限 (MODE
)。
1. 找到当前用户的用户名
在终端运行:
whoami
假设返回:
yourusername
你的用户名是 yourusername
。
2. 编辑 udev
规则
创建或修改 /etc/udev/rules.d/99-hello_world.rules
:
sudo nano /etc/udev/rules.d/99-hello_world.rules
添加以下内容:
KERNEL=="hello_world", OWNER="yourusername", GROUP="yourusername", MODE="0666"
解释:
OWNER="yourusername"
→ 设备的所有者设置为yourusername
。GROUP="yourusername"
→ 设备的用户组设置为yourusername
。MODE="0666"
→ 允许所有用户(包括非root
用户)读写设备。
在命令 sudo nano /etc/udev/rules.d/99-hello_world.rules
中,99-hello_world.rules
是一个 udev 规则文件的名称。以下是对这个文件及其命名的详细解析:
udev规则文件路径:
/etc/udev/rules.d/ —本机规则
/lib/udev/rules.d/ —系统规则
udev 规则文件
- udev:是 Linux 系统中的设备管理器,负责动态管理设备节点。它在设备添加或移除时自动执行规则,以便配置设备的属性和行为。
文件路径
/etc/udev/rules.d/
:这是存放 udev 规则的目录。所有以.rules
结尾的文件都包含了设备管理的规则。
文件命名
99-hello_world.rules
:- 数字前缀(
99
):这个数字决定了规则的加载顺序。数字越小,规则越早加载。通常,99 表示这是一个优先级较低的规则,允许其他规则先执行。 - 文件名(
hello_world
):这个部分通常用来描述规则的用途或关联的设备。可以是任何有意义的名称,便于识别。
- 数字前缀(
规则的作用
在 99-hello_world.rules
文件中,你可以定义针对特定设备的规则,例如:
- 设置设备权限。
- 创建符号链接。
- 运行特定的脚本或命令。
示例内容
以下是一个简单的 99-hello_world.rules
文件示例:
SUBSYSTEM=="usb", ATTR{idVendor}=="1234", ATTR{idProduct}=="5678", MODE="0666"
这个规则的意思是:
- 当连接的设备属于 USB 子系统(
SUBSYSTEM=="usb"
)。 - 设备的供应商 ID 为
1234
,产品 ID 为5678
。 - 设置设备的权限为
0666
(所有用户可读可写)。
如果你的 username
可能会变,可以改成 plugdev
组(Linux 里用于管理可插拔设备):
KERNEL=="hello_world", GROUP="plugdev", MODE="0660"
然后把你的用户加到 plugdev
组:
sudo usermod -aG plugdev yourusername
3. 重新加载 udev
规则
执行以下命令,使新的规则生效:
sudo udevadm control --reload-rules
sudo service udev restart
sudo udevadm trigger
4. 重新加载驱动
sudo rmmod hello_world
sudo modprobe hello_world
或者直接重启:
sudo reboot
5. 验证权限
启动后,运行:
ls -l /dev/hello_world
期望输出:
crw-rw-rw- 1 yourusername yourusername 240, 0 Feb 7 12:00 /dev/hello_world
或:
crw-rw---- 1 root plugdev 240, 0 Feb 7 12:00 /dev/hello_world
如果你使用了
plugdev
组,确保你的用户在plugdev
组里:groups yourusername
6. 运行应用程序(不需要 sudo
)
现在,你的应用可以直接访问 /dev/hello_world
了:
cat /dev/hello_world
不再需要 sudo
🎉🚀!
总结
- 修改
udev
规则 让/dev/hello_world
归属于当前用户或plugdev
组。 - 重新加载
udev
规则 并 重启驱动 使其生效。 - 验证权限,确保
/dev/hello_world
可由普通用户访问。 - 你的应用可以直接访问设备,无需
sudo
了!
这样,你的 Linux 驱动就真正 开机自动加载 + 用户无需 root 访问 了! 🎉