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

本文介绍Linux系统中通过UEvent创建设备文件的过程。包括如何根据UEvent信息确定设备文件的位置、权限(uid, gid, mode)及创建过程。重点介绍了make_device函数及get_device_perm函数的应用。

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

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.[luther.gliethtt]
于是在devperms[]数组中我们就可以方便的控制生成的/dev文件的uid,gid和mode了.[luther.gliethtt]

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 },
}
;

<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`。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值