主设备号和file_operations结构关系; ioctl和unlocked_ioctl, ioctl简单测试例子

本文深入探讨了Linux内核中ioctl与unlocked_ioctl的使用场景与区别,解释了为何在某些情况下需要使用unlocked_ioctl替代ioctl,并提供了在应用程序中测试这些函数的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

open一个字符设备的过程是从
VFS层open系统调用 ===> def_chr_fops-->open ===> cdev->fops->open ===> device specific operation

如果是同一类设备,比如系统中有多个framebuffer,那么就把共有的操作放在cdev->fops->open ,这时向系统注册的仅仅是这一个驱动程序;
将这类主设备号相同的一类设备(如/dev/fb0 /dev/fb1等)放在一个数组中,然后在cdev->fops->open 根据次设备号作为Index来调用体现不同设备差异的那些操作,即 device specific operation , 这就是为什么framebuffer/block 等硬件驱动中实现的都不是file_operations这种操作,而是跟具体设备相关的那些操作。
这就是所谓的一类设备都应一种驱动。


可以认为大多数简单的设备,仅仅需要实现到cdev->fops->open 这个位置,等他们变得复杂,能够提炼出共性的时候就 需要实现到[红色device specific operation 这个位置。

 

对于ioctl操作,优先执行f_op->unlocked_ioctl,如果没有unlocked_ioctl,那么执行f_op->ioctl


在学习ARM开发中.ioctl在内2.6.32中。file_operatioins中还有ioctl.自己升级到2.6.36时,使用vim 查错源代码时,发现没有ioctl只有unlocked_ioctl与compat_ioctl函数了。

自己改写这个unlocked_ioctl函数,但是,不知如何用应用程序测试,
当在内核使用ioctl函数时,我在应用程序中使用ioctl(fd,"on"来测试是否调用了ioctl函数。
可是改成unlocked_ioctl函数时,使用unlocked_ioctl()函数时,提示没有这个函数,请问怎么办?
long  test_ioctl(struct file *filp,unsigned int cmd,unsigned long value){
        struct newdev *p = filp->private_data;
        printk("ioctl %X \n",p);
        switch(cmd){
                case 0:
                        p->on();break;
                case 1:
                        p->off();break;
                default:
                        printk("Unknow cmd\n";
                        return -EINVAL;
        }
        return 0;
}

struct file_operations fops = {
        .open = test_open,
        .unlocked_ioctl = test_ioctl,
};

void test_on(void){
        printk("test_on test\n";
}

void test_off(void){
        printk("test_off test \n";
}

void initial_device(struct newdev *d){
        d->on = test_on;
        d->off = test_off;
}


应用程序如下:

int main(int argc,char *argv[]){

        int fd;
        char buf[10];
        printf("1\n";
        printf("argc[%d]\n",argc);
        if (argc != 2){
                printf("Usage %s string \n",argv[0]);
                return -1;
        }
        printf("2\n";

        fd = open("/dev/test",O_RDWR);
        printf("3\n";

        if (fd < 0 ){
                printf("fd open errror\n";
                return -1;

        }
        printf("4\n";
        if (!strncasecmp(argv[1],"on",2))
                unlocked_ioctl(fd,0);
        if (!strncasecmp(argv[1],"off",3))
                unlocked_ioctl(fd,1);

        return 0;
}

root@xu-laptop:/usr/src/linux-2.6.36/drivers/new/10th# gcc -o app app.c
/tmp/cc4PWyCc.o: In function `main':
app.c.text+0xf1): undefined reference to `unlocked_ioctl'
app.c.text+0x12a): undefined reference to `unlocked_ioctl'
collect2: ld returned 1 exit status

root@xu-laptop:/usr/src/linux-2.6.36/drivers/new/10th# 为了把BKL从内核中慢慢去掉,加入了unlocked_ioctl,但是有很多地方都用到ioctl,所以一直保留着,直到b19dd42f的时候,最后移除了所有对ioctl的使用,才把ioctl从file_operations里面去掉。


所以你在内核里用unlocked_ioctl是没有问题的。

但是你应用程序里面不能用unlocked_ioctl,因为系统调用ioctl是没有改变的,还是原来的系统调用接口,只是系统调用的实现中,vfs_ioctl()变成了unlocked_ioctl,在应用层你根本不用关注内核中的这些实现上的改变,你只需要按照系统调用的用法用就可以了。

所以,在内核module里,你应该用.unlocked_ioctl = test_ioctl;

而在应用层测试的时候,应用的系统调用仍然是ioctl()。

也就时VFS层这个中间虚拟文件层调用我写的unlocked_ioctl,而应用层还是通过ioctl调用VFS里面的函数,我要做的工作由VFS完成了。


<think>嗯,用户这次问的是Linux内核开发中unlocked_ioctlcompat_ioctl的区别使用方法。从引用内容看,用户已经查阅了相关资料,但需要更系统化的解释。用户可能是内核驱动开发者,正面临兼容性处理问题。注意到用户引用了三个关键资料:引用1提到内核2.6.35后淘汰了传统ioctl;引用2说明内核社区对ioctl的态度;引用3展示了驱动代码中的典型用法。用户真正需要的是实践指导而非理论概述。之前用户的问题集中在系统编程接口演变,这次更聚焦具体函数实现差异。需要特别注意:1)锁机制差异必须强调BigKernelLock的淘汰背景;2)32/64位兼容性问题要举出指针转换实例;3)必须澄清常见误解——compat_ioctl不只服务于32位用户空间。用户代码片段里drv_ioctl同时挂到两个钩子,这很典型但需提醒CONFIG_COMPAT条件编译的风险。还应该补充注意事项,比如cmd编号定义技巧(避免混用旧版命令)。用户明显需要立即应用到代码中,所以示例比理论更重要。要不要提ioctl替代方案?比如sysfs或netlink?虽然用户没问,但引用2提到内核开发者不推荐ioctl,或许值得在注意事项里暗示。不过当前问题焦点还是在这两个接口区别上。</think> 理解正确,`unlocked_ioctl` `compat_ioctl` 是 Linux 内核驱动程序 `struct file_operations` 中的关键方法接口。它们主要用于处理设备相关的控制命令(`ioctl`)。以下是它们的核心区别使用方法: --- ### **1. `unlocked_ioctl`(核心接口)** * **用途**:处理设备控制命令的主接口。 * **参数**: ```c long (*unlocked_ioctl)(struct file *file, unsigned int cmd, unsigned long arg); ``` - `file`:打开的文件描述符 - `cmd`:用户空间传递的命令号 - `arg`:命令参数(用户空间指针) * **特点**: - **无大内核锁(BKL)**:与旧版 `ioctl` 不同,它无需持有 BKL(Big Kernel Lock),因此性能更好且支持细粒度锁策略[^1][^2]。 - **运行在调用进程的上下文**:可直接访问用户空间内存(需用 `copy_from_user()`/`copy_to_user()`)。 - **现代驱动必须实现**:自内核 2.6.35 后,它是所有新型驱动程序的默认接口。 --- ### **2. `compat_ioctl`(兼容接口)** * **用途**:处理 **32位用户程序** 在 **64位内核** 上发出的 `ioctl` 请求。 * **参数**: ```c long (*compat_ioctl)(struct file *file, unsigned int cmd, unsigned long arg); ``` - 参数形式与 `unlocked_ioctl` 相同,但 `arg` 是 **32位地址**。 * **核心问题**: - 当 64 位内核运行 32 位程序时,用户空间指针为 32 位(4 字节),但内核期待 64 位指针。 - 若不加处理,直接传递 `arg` 给 `unlocked_ioctl` 会导致指针截断错误。 * **解决方案**: - 实现 `compat_ioctl`,在其中将 32 位地址转换并复制到内核可识别的 64 位结构体。 - 使用兼容函数如 `compat_ptr(arg)` 将 `arg` 转为有效 64 位指针。 - 需为每个 `cmd` 额外实现兼容数据处理(如定义 `struct foo_compat`)。 --- ### **3. 关键区别总结** | **特性** | `unlocked_ioctl` | `compat_ioctl` | |----------------------|--------------------------------|--------------------------------| | 目标用户空间 | 所有程序(原生架构) | 32 位程序(在 64 位内核运行) | | `arg` 指针大小 | 原生地址大小(64 位系统为 8 字节) | 固定 4 字节(32 位地址) | | 是否需数据转换 | 否 | 是(需处理 32/64 位数据差异) | | BKL 锁 | 不使用 | 不使用 | | 内核版本要求 | ≥ 2.6.11 | 需开启 `CONFIG_COMPAT` | --- ### **4. 驱动实现示例** ```c // 处理通用 ioctl 请求 long my_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { case MY_CMD_1: // 处理原生数据(64 位或 32 位内核上的本地程序) struct data d; copy_from_user(&d, (void __user *)arg, sizeof(d)); // ... 执行操作 break; // ... 其他命令 } return 0; } // 处理 32 位程序的 ioctl 请求 #ifdef CONFIG_COMPAT long my_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { case MY_CMD_1: // 1. 将 arg 转为兼容指针 struct compat_data __user *udata32 = compat_ptr(arg); // 2. 定义 32 位兼容结构struct compat_data d32; // 3. 从 32 位用户空间拷贝数据 copy_from_user(&d32, udata32, sizeof(d32)); // 4. 将数据转换为原生格式(例如扩宽整数) struct data d = { .field1 = d32.field1, // ... 其他字段转换 }; // 5. 复用通用处理逻辑 return my_unlocked_ioctl(file, cmd, (unsigned long)&d); break; } return -ENOTTY; } #endif struct file_operations fops = { .owner = THIS_MODULE, .unlocked_ioctl = my_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = my_compat_ioctl, // 注册兼容接口 #endif }; ``` --- ### **5. 注意事项** 1. **命令号兼容性**: - 使用 `_IOC()`/`_IOR()` 等宏定义 `cmd` 时需确保其数值在 32/64 位架构下一致。 - 可显式指定命令号类型为 `unsigned int`。 2. **数据对齐差异**: - 32 位结构体可能有不同的对齐方式(例如 `u64` 在 32 位系统可能为 4 字节对齐)。 - 使用 `__attribute__((packed))` 或内核提供的兼容数据类型(如 `compat_ulong_t`)。 3. **可选实现**: - 若驱动不涉及指针或数据结构变化(如只传递整数的命令),可省略 `compat_ioctl`。 - 若不实现 `compat_ioctl`,内核会尝试将 32 位参数直接传递给 `unlocked_ioctl`,**可能导致崩溃或数据错误!** 4. **优先使用标准框架**: - 对于常规设备控制,推荐优先使用 `sysfs`、`debugfs` 或 `netlink` 等更现代的内核接口(替代过度依赖 `ioctl`)[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值