浅析dev目录下设备文件mknod节点gid,uid和mode的如何方便设置

本文介绍了Linux系统中通过UEvent机制自动创建设备节点的过程。根据UEvent信息判断设备类型,并在相应的目录下创建字符设备或块设备节点。此外,还详细展示了如何根据不同设备路径配置权限、所有者等属性。

init
= > handle_device_fd 收到uevent信息
= > handle_device_event
= >

static void handle_device_event(struct uevent *uevent) { ... /* are we block or char? where should we live? */ if(!strncmp(uevent->path, "/block", 6)) { block = 1; base = "/dev/block/";//根据uevent路径改变该节点路径 mkdir(base, 0755); } else { block = 0; /* this should probably be configurable somehow */ if(!strncmp(uevent->path, "/class/graphics/", 16)) { base = "/dev/graphics/";//根据uevent路径改变该uevent需要创建节点的路径 mkdir(base, 0755); } else if (!strncmp(uevent->path, "/class/oncrpc/", 14)) { base = "/dev/oncrpc/"; mkdir(base, 0755); } else if (!strncmp(uevent->path, "/class/adsp/", 12)) { base = "/dev/adsp/"; mkdir(base, 0755); } else if(!strncmp(uevent->path, "/class/input/", 13)) { base = "/dev/input/";//根据uevent路径改变该uevent需要创建节点的路径 mkdir(base, 0755); } else if(!strncmp(uevent->path, "/class/sensors/", 15)) { base = "/dev/sensors/"; mkdir(base, 0755); } else if(!strncmp(uevent->path, "/class/mtd/", 11)) { base = "/dev/mtd/";//根据uevent路径改变该uevent需要创建节点的路径 mkdir(base, 0755); } else if(!strncmp(uevent->path, "/class/misc/", 12) && !strncmp(name, "log_", 4)) { base = "/dev/log/";//根据uevent路径改变该uevent需要创建节点的路径 mkdir(base, 0755); name += 4; } else if(!strncmp(uevent->path, "/class/sound/", 13)) { base = "/dev/snd/"; mkdir(base, 0755); } else base = "/dev/"; } snprintf(devpath, sizeof(devpath), "%s%s", base, name); if(!strcmp(uevent->action, "add")) { make_device(devpath, block, uevent->major, uevent->minor);//创建节点文件文件devpath return; } ... } static void make_device(const char *path, int block, int major, int minor) { unsigned uid; unsigned gid; mode_t mode; dev_t dev; if(major > 255 || minor > 255) return; mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);//获取将要创建的节点是否需要重设它的mode数值 dev = (major << 8) | minor; mknod(path, mode, dev); chown(path, uid, gid); }

 

get_device_perm函数会比较path路径是否和下面的devperms[ ] 数组中的inode路径相一致, 如果一致, 那么返回devperms[ ] 数组中指定的uid, gid和mode数值, 这样make_device就会向/ dev这个tmpfs'内存虚拟物理盘' 创建inode节点, 同时改变该inode的uid和gid.
于是在devperms[ ] 数组中我们就可以方便的控制生成的/ dev文件的uid, gid和mode了.

static struct perms_ devperms[] = { { "/dev/null", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/zero", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/full", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/ptmx", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/tty", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/random", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/urandom", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/ashmem", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/binder", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/m2d", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/bmm", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/innodev", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/innodev0", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/innodev1", 0666, AID_ROOT, AID_ROOT, 0 }, { "/dev/innodev2", 0666, AID_ROOT, AID_ROOT, 0 }, /* logger should be world writable (for logging) but not readable */ { "/dev/log/", 0662, AID_ROOT, AID_LOG, 1 }, /* these should not be world writable */ { "/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 }, { "/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 }, { "/dev/ttyMSM0", 0660, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, { "/dev/alarm", 0664, AID_SYSTEM, AID_RADIO, 0 }, { "/dev/tty0", 0666, AID_ROOT, AID_SYSTEM, 0 }, { "/dev/graphics/", 0666, AID_ROOT, AID_GRAPHICS, 1 }, { "/dev/hw3d", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, { "/dev/input/", 0660, AID_ROOT, AID_INPUT, 1 }, { "/dev/eac", 0660, AID_ROOT, AID_AUDIO, 0 }, { "/dev/cam", 0660, AID_ROOT, AID_CAMERA, 0 }, { "/dev/video0", 0660, AID_ROOT, AID_CAMERA, 0 }, { "/dev/mved", 0660, AID_SYSTEM, AID_CAMERA, 0 }, { "/dev/pmem", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, { "/dev/pmem_gpu", 0660, AID_SYSTEM, AID_GRAPHICS, 1 }, { "/dev/pmem_adsp", 0660, AID_SYSTEM, AID_AUDIO, 1 }, { "/dev/pmem_camera", 0660, AID_SYSTEM, AID_CAMERA, 1 }, { "/dev/oncrpc/", 0660, AID_ROOT, AID_SYSTEM, 1 }, { "/dev/adsp/", 0660, AID_SYSTEM, AID_AUDIO, 1 }, { "/dev/sensors/", 0666, AID_SYSTEM, AID_SYSTEM, 1 }, { "/dev/mt9t013", 0660, AID_SYSTEM, AID_SYSTEM, 0 }, { "/dev/akm8976_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 }, { "/dev/akm8976_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 }, { "/dev/akm8976_pffd", 0640, AID_COMPASS, AID_SYSTEM, 0 }, { "/dev/msm_pcm_out", 0660, AID_SYSTEM, AID_AUDIO, 1 }, { "/dev/msm_pcm_in", 0660, AID_SYSTEM, AID_AUDIO, 1 }, { "/dev/msm_pcm_ctl", 0660, AID_SYSTEM, AID_AUDIO, 1 }, { "/dev/msm_mp3", 0660, AID_SYSTEM, AID_AUDIO, 1 }, { "/dev/smd0", 0640, AID_RADIO, AID_RADIO, 0 }, { "/dev/qmi", 0640, AID_RADIO, AID_RADIO, 0 }, { "/dev/qmi0", 0640, AID_RADIO, AID_RADIO, 0 }, { "/dev/qmi1", 0640, AID_RADIO, AID_RADIO, 0 }, { "/dev/qmi2", 0640, AID_RADIO, AID_RADIO, 0 }, { "/dev/htc-acoustic", 0640, AID_RADIO, AID_RADIO, 0 }, { "/dev/snd/", 0666, AID_ROOT, AID_AUDIO, 1 }, { NULL, 0, 0, 0, 0 }, };

 

    http://blog.chinaunix.net/space.php?uid=20564848&do=blog&cuid=1348852

<think>我们正在讨论在Linux设备驱动开发中,如何在/dev目录下创建子目录并生成设备节点文件。 根据引用[1]引用[2]的内容,我们知道设备节点文件的创建有两种方式:手动创建(mknod命令)自动创建(通过udev/mdev机制)。 但是用户要求的是在/dev目录下创建子目录,并在该子目录下生成设备节点。这意味着我们需要在自动创建设备节点时,指定设备节点所在的子目录。 自动创建设备节点通常使用udev(或嵌入式系统中的mdev)机制。在驱动程序中,我们通过device_create函数创建设备节点,该函数可以指定设备节点所在的目录。 步骤: 1. 首先,我们需要创建一个class(类),这个类会在/sys/class/下生成一个目录。 2. 然后,在创建device(设备)时,我们可以指定设备节点在/dev目录下的路径,包括子目录。 具体来说: - 使用class_create创建一个类,例如:my_class = class_create(THIS_MODULE, "my_class"); - 然后,使用device_create创建设备,并指定设备节点所在的子目录device_create函数的原型为: struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...); 其中,parent参数可以指定父设备,如果我们要在/dev下创建一个子目录,那么我们可以通过指定parent为一个目录设备来实现。但更简单的方法是,在device_create函数中直接指定设备节点的路径,这可以通过fmt参数来实现,例如:device_create(my_class, NULL, devt, NULL, "my_subdir/my_device"); 这样,设备节点就会出现在/dev/my_subdir/my_device。 但是,请注意:在较新的内核中,device_create函数并不直接支持在设备名中包含路径。实际上,device_create函数创建的设备节点默认在/dev目录下,且不能直接创建子目录。 因此,我们需要使用另一种方法:在创建class的时候,设置class的devnode回调函数,该回调函数可以指定设备节点的路径。 具体步骤(适用于较新内核): 1. 创建class时,除了使用class_create,还需要设置class的devnode属性。 2. 定义一个devnode回调函数,该函数返回设备节点的路径(包括子目录)。 示例代码: 在驱动程序中: static char *devnode(struct device *dev, umode_t *mode) { if (mode) *mode = 0666; // 设置设备节点的权限 // 返回设备节点的路径,这里我们返回一个字符串,例如"my_subdir/my_device" return kasprintf(GFP_KERNEL, "my_subdir/%s", dev_name(dev)); } 然后,在创建类之后,设置类的devnode回调: my_class = class_create(THIS_MODULE, "my_class"); if (IS_ERR(my_class)) { // 错误处理 } my_class->devnode = devnode; 这样,当设备被创建时,设备节点就会出现在/dev/my_subdir/目录下,设备节点名为my_device(这里假设设备名为my_device,实际名字由dev_name(dev)决定,也可以在device_create时指定)。 注意:dev_name(dev)返回的是设备的名字,这个名字是在device_create时通过格式化字符串指定的,例如:device_create(my_class, NULL, devt, NULL, "my_device"),那么dev_name(dev)返回的就是"my_device"。 因此,通过设置class的devnode回调,我们可以自定义设备节点在/dev目录下的位置,包括子目录。 另外,确保在卸载驱动时,删除设备类: device_destroy(my_class, devt); class_destroy(my_class); 关于引用[2]中提到的问题:在/sys/class/下没有生成设备信息,可能是因为没有正确创建类设备。使用上述方法,应该会在/sys/class/my_class/下生成设备的信息,并且在/dev/my_subdir/下生成设备节点。 但是,请注意,/dev下的子目录需要事先存在吗?不需要,udev/mdev会自动创建不存在的目录。 因此,驱动程序的编写步骤: 1. 定义devnode回调函数,返回包含子目录的路径。 2. 创建class,并设置class->devnode为这个回调函数。 3. 使用device_create创建设备(此时设备名不包含路径,因为路径由devnode回调指定)。 4. 在卸载驱动时,删除设备并销毁类。 示例代码片段: ``` #include <linux/device.h> static char *my_devnode(struct device *dev, umode_t *mode) { if (mode) *mode = 0666; return kasprintf(GFP_KERNEL, "my_subdir/%s", dev_name(dev)); } static struct class *my_class; static int __init my_init(void) { // ... 分配设备号等操作 my_class = class_create(THIS_MODULE, "my_class"); if (IS_ERR(my_class)) { // 错误处理 return PTR_ERR(my_class); } my_class->devnode = my_devnode; // 创建设备 device_create(my_class, NULL, devt, NULL, "my_device"); return 0; } static void __exit my_exit(void) { device_destroy(my_class, devt); class_destroy(my_class); } module_init(my_init); module_exit(my_exit); ``` 这样,在加载模块后,应该会在/dev/my_subdir/下看到设备节点my_device。 注意:如果使用mdev(busybox的udev简化版),需要确保在根文件系统的/etc/init.d/rcS脚本中执行了: echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s 并且内核配置中已经支持sysfsdevtmpfs(或udev)。 如果遇到问题,可以检查/sys/class/下是否有创建的类(my_class),以及类下面是否有设备(my_device)的信息。如果没有,可能是设备创建失败。 另外,引用[2]中提到的问题,如果/sys/class下没有生成设备信息,那么可能是class_create或者device_create调用失败。需要检查返回值。 最后,用户问题要求的是在开发目录下创建子目录,但根据上下文,我们理解为在/dev目录下创建子目录。如果是其他目录,可能需要调整,但方法类似。 生成相关问题: 1. 如何在设备驱动中设置设备节点的权限? 2. 如何调试设备节点创建失败的问题? 3. udevmdev有什么区别? 4. 在驱动中创建设备节点时,如何指定多个子目录? 5. 设备节点的主次设备号如何分配?</think>在 Linux 设备驱动开发中,要在 `/dev` 目录下创建子目录并生成设备节点,需结合 **class 创建** **udev/mdev 规则** 实现。以下是具体步骤: --- ### 一、驱动程序中创建设备类与子目录 在驱动代码中,通过 `class_create()` 创建类,并在 `device_create()` 中指定子目录路径: ```c #include <linux/device.h> // 1. 创建设备类 static struct class *my_class; my_class = class_create(THIS_MODULE, "my_class"); // 2. 创建设备节点并指定子目录路径 struct device *dev; dev = device_create(my_class, NULL, devt, NULL, "my_subdir/my_device"); ``` - `my_subdir`:在 `/dev` 下自动创建的子目录名 - `my_device`:设备节点名称(如:`/dev/my_subdir/my_device`) --- ### 二、配置 udev/mdev 规则(关键) 若设备节点未自动生成,需配置 udev/mdev 规则: #### 1. **检查 `/sys/class/` 中的设备信息** - 驱动加载后,确认 `/sys/class/my_class/my_device` 目录存在 - 检查该目录下是否包含 `dev` 文件(包含主次设备号) #### 2. **添加 udev 规则** 在 `/etc/udev/rules.d/` 创建规则文件(如 `99-my-device.rules`): ```bash # 匹配设备类,在 /dev 下创建子目录节点 SUBSYSTEM=="my_class", ACTION=="add", RUN+="/bin/mkdir -p /dev/my_subdir" SUBSYSTEM=="my_class", ACTION=="add", PROGRAM="/bin/device_name %k", SYMLINK+="my_subdir/%c" ``` #### 3. **嵌入式系统(使用 mdev)** 在 `/etc/mdev.conf` 中添加: ```bash # 格式:<设备正则> <用户>:<组> <权限> [@|$|*]<命令> my_device[0-9]* 0:0 0660 @mkdir -p /dev/my_subdir && ln -sf $MDEV /dev/my_subdir/$MDEV ``` --- ### 三、验证流程 1. 加载驱动模块: ```bash insmod my_driver.ko ``` 2. 检查节点生成: ```bash ls /dev/my_subdir # 应显示设备节点 my_device ls /sys/class/my_class/my_device # 检查 sysfs 信息 ``` 3. 卸载驱动时自动清理: ```c // 驱动代码中需添加清理逻辑 device_destroy(my_class, devt); class_destroy(my_class); ``` --- ### 常见问题解决 1. **节点未生成**: - 确认 `CONFIG_UEVENT_HELPER=y` 内核配置已启用 - 检查 `/sys/class/` 是否存在设备信息(若无,说明 `device_create()` 失败)[^2] - 执行 `mdev -s` 或 `udevadm trigger` 强制重新扫描设备 2. **权限问题**: - 在 udev/mdev 规则中指定权限(如 `0666`) 3. **子目录未创建**: - 确保 udev/mdev 规则中的 `mkdir -p` 命令正确执行 - 手动测试规则:`udevadm test /sys/class/my_class/my_device` --- ### 示例完整流程 ```c // 驱动代码片段 static int __init my_init(void) { my_class = class_create(THIS_MODULE, "my_class"); device_create(my_class, NULL, MKDEV(250, 0), NULL, "my_subdir/led"); return 0; } static void __exit my_exit(void) { device_destroy(my_class, MKDEV(250, 0)); class_destroy(my_class); } ``` 加载驱动后,设备节点将出现在 `/dev/my_subdir/led`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值