内核新的ioctl方式--unlocked_ioctl和compat_ioctl(解决error:unknown field 'ioctl' specified in initializer)

本文解析了Linux内核中ioctl接口的变化,特别是从2.6.36版本开始的重大改动,介绍了如何将旧版本驱动中的ioctl接口更新为新的unlocked_ioctl和compat_ioctl形式,并提供了具体的代码示例。
优快云GitHub
内核新的ioctl方式–unlocked_ioctl和compat_ioctl
解决error:unknown field ‘ioctl’ specified in initializer
LDD/problem/port/ioctl


知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作


1 问题


把早期 2.6.32之前的驱动移植到新的内核中, 如果驱动中定义了 ioctl 接口, 老是会提示如下错误

error:unknown field ‘ioctl’ specified in initializer

2 原因


单从字面上看, 可以看出是目前我们驱动中定义的 ioctl 接口与内核中 file_operations 结构 ioctl 函数的定义接口不同.

那么内核到底中到底经历了什么呢?

去查看 file_operations 结构体的定义, 可以发现原因是 :

2.6.36 内核上 file_operations 发生了重大的改变 :

原先的, 参见include/linux/fs.h, version 2.6.17, line 1015

int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long);

被改为了, 参见include/linux/fs.h, version 4.11, line 1654,

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

具体 file_operations 的实现可以参见include/linux/fs.h, version 4.11, line 1654,

邮件列表参见The new way of ioctl()

早期为了保证兼容性, file_operations 结构体中仍然包含 ioctl 函数指针成员, 但是在 kernel 3.0 中已经完全删除了 struct file_operations 中的 ioctl 函数指针

3 解决方案


因而在实际驱动中, 我们需要

  • 将原先的写的 ioctl 函数声明给改成下面的 unlocked_ioctl 或者 compat_ioctl,

  • file_operations 结构体的初始化中也是一样. 修改为unlocked_ioctl 或者 compat_ioctl,

  • 注意参数的兼容性问题, 新的ioctl() 接口没有 struct inode* 参数, 如果ioctl 接口中使用了 inode, 因此需要通过其他方式获取 inode

内核提供了接口 file_inode 来通过文件指针 file 来获取其 inode 信息, 该函数定义在include/linux/fs.h, version 4.11, line 1213, 如下所示

static inline struct inode *file_inode(const struct file *f)
{
    return f->f_inode;
}

因此解决方案如下 :

  1. 首先是将 ioctl 的实现转换为 unlock_ioctl
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
static int XXX_ioctl(
        struct inode *indoe,
        struct file *filp,
        unsigned int cmd,
        unsigned long arg)
{
#else
//long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//long (*compat_ioctl) (struct file *, unsigned int cmd, unsigned long arg)
static long XXX_unlocked_ioctl(
        struct file *filp,
        unsigned int cmd,
        unsigned long arg)
{
    //struct inode *inode = filp->f_dentry->d_inode;
    //struct inode *inode = filp->d_inode;
    struct inode *inode = inode = file_inode(filp);
#endif
    /*  此处是ioctl() 函数结构的具体实现  */
}

file_operations 结构体初始化的过程采取同样的操作

static struct file_operations fpga_fops = {
    .owner  = THIS_MODULE,
    .open   = fpga_open,
    .read   = fpga_read,
    .write = fpga_write,
    .llseek = fpga_llseek,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
    .ioctl = XXX_ioctl,
#else
    .unlocked_ioctl = XXX_unlocked_ioctl,
#endif
};

4 参照


ioctl 变成了 unlocked_ioctl

Linux字符设备驱动入门(二)——加入ioctl功能

内核新的ioctl方式—- unlocked_ioctl和compat_ioctl

error: unknown field ‘ioctl’ specified in initializer

error: unknown field ‘ioctl’ specified in initializer

error: unknown field ‘ioctl’ specified in initializer

error:unknown field ‘ioctl’ specified in initializer

error: unknown field ‘ioctl’ specified in initializer

error: unknown field ‘ioctl’ specified in initializer问题


知识共享许可协议本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作.

### 功能与使用场景分析 `unlocked_ioctl` `compat_ioctl` 是 Linux 内核中用于处理设备特定 I/O 控制命令的两个函数指针,它们定义在 `struct file_operations` 结构体中。这两个函数的主要区别在于它们的设计目的使用场景。 #### unlocked_ioctl `unlocked_ioctl` 函数的设计目的是为了替代传统的 `ioctl` 函数,以提供更好的并发性减少锁竞争。在 `unlocked_ioctl` 中,不再需要传递 `inode` 参数,因为可以通过 `file` 结构体中的 `f_dentry` 字段获取到 `inode` 信息。此外,在调用 `unlocked_ioctl` 之前,不会自动获取大内核锁(Big Kernel Lock, BKL),这意味着驱动程序或文件系统需要自行处理同步问题。对于新编写的代码,推荐使用 `unlocked_ioctl`,因为它允许更细粒度的锁定机制,从而提高系统的整体性能 [^3]。 #### compat_ioctl `compat_ioctl` 函数则是为了支持 32 位应用程序在 64 位内核上的运行而设计的。当 32 位应用程序尝试通过 `ioctl` 系统调用来与设备交互时,内核会调用 `compat_ioctl` 函数来处理这些请求。`compat_ioctl` 函数通常会将 32 位的参数转换为 64 位格式,然后调用 `unlocked_ioctl` 函数来执行实际的操作。这样可以确保 32 位应用程序能够在 64 位内核上正常工作,同时保持与 64 位应用程序相同的设备访问能力 [^2]。 ### 使用场景 - **unlocked_ioctl** 适用于所有新的驱动程序开发现有驱动程序的更新。由于其不需要 BKL 并且提供了更灵活的锁定机制,因此更适合现代多线程多处理器环境下的设备驱动开发 [^3]。 - **compat_ioctl** 主要用于需要支持 32 位应用程序的场景。如果驱动程序需要在 64 位内核上支持 32 位的应用程序,那么就需要实现 `compat_ioctl` 函数。否则,可以省略 `compat_ioctl` 的实现 [^4]。 ### 示例代码 以下是一个简单的示例,展示了如何在驱动程序中实现 `unlocked_ioctl` `compat_ioctl`: ```c #include <linux/fs.h> #include <linux/ioctl.h> static long drv_ioctl(struct file *file, unsigned int ioctl_code, unsigned long arg) { // 处理 ioctl 请求 return 0; } static struct file_operations file_operations = { .owner = THIS_MODULE, .open = drv_open, .release = drv_release, .read = drv_read, .unlocked_ioctl = drv_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = drv_ioctl, #endif .mmap = drv_mmap, }; ``` 在这个示例中,`drv_ioctl` 函数同时被赋值给 `unlocked_ioctl` `compat_ioctl`,这意味着无论是 32 位还是 64 位的应用程序,都可以使用相同的处理逻辑来执行 I/O 控制命令 [^4]。 ### 总结 `unlocked_ioctl` `compat_ioctl` 都是 Linux 内核中用于处理设备特定 I/O 控制命令的函数指针,但它们的设计目的使用场景有所不同。`unlocked_ioctl` 适用于新代码的开发,提供了更好的并发性性能;而 `compat_ioctl` 则主要用于支持 32 位应用程序在 64 位内核上的运行 [^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值