对 ioctl() 和内核的混淆 headers

Confusion over ioctl() and kernel headers

据我所知,ioctl() 用于向用户空间应用程序公开 "extended" 系统调用接口。 ioctl() 不是添加数千个特定驱动程序独有的系统调用,而是用于通过单个系统调用提供可扩展的 driver-specific 功能。

这似乎很清楚。但是,我正在尝试编译我的第一个使用 ioctl() 调用的应用程序,我开始怀疑我的理解。

具体来说,我想对 "sanitize" 一个 eMMC 设备进行 ioctl() 调用。查看 /usr/include/linux/mmc/ioctl.h(或内核源代码 include/uapi/linux/mmc/ioctl.h),我可以看到这个结构:

struct mmc_ioc_cmd {
        // Most fields omitted
        int write_flag;    
        __u32 opcode;
        __u32 arg;
};

从用户空间,我没有任何问题,包括这个 header 并将这个结构传递到我的 ioctl() 调用中。

这就是我最终的清理代码片段的样子:

int sanitize(int fd)
{
  struct mmc_ioc_cmd command;
  memset(&command, 0, sizeof(command));

  command.write_flag = 1;
  command.opcode = MMC_SWITCH;
  command.arg = EXT_CSD_SANITIZE_START << 16;

  return ioctl(fd, MMC_IOC_CMD, &command);
}

我的问题是 MMC_SWITCHEXT_CSD_SANITIZE_START 都在内核 header 中定义。具体来说,在我的内核源代码中,它们都位于 include/linux/mmc/mmc.h

我在 Internet 上看到的所有内容都说 not 在构建用户空间项目时包含来自内核源代码的 headers。那样的话,如何合理使用MMCioctl()?内核公开要传递给 ioctl() 的结构,但似乎您只能通过在内核源代码中隐藏的 "hidden" 常量填充它来使用该结构。

我目前的解决方案是将必要的常量从内核 headers 复制到我自己的项目中,但这对我来说感觉很脏。

我是否将 use-case 误解为 ioctl()?这是设计疏忽吗?

如果您可以 #include 它而不添加任何额外的 -I 在您的 GCC 命令行上包含路径,那么您就可以了。

Everything I've seen on the internet says to not include headers from the kernel source when building userspace projects.

该建议意味着不要直接从内核源代码树中包含 headers。 uapi headers 旨在从用户空间使用,并安装到 /usr/include

MMC_IOC_CMD ioctl 和相应的 mmc_ioc_cmd 结构是 Linux 用户空间 API 的一部分,因此在 uapi 中定义header 安装到 /usr/include

您在 opcode 字段中输入的值会直接发送到设备。内核并不真正关心它是什么,也不能保证设备支持什么操作码,或者它对任何特定操作码的行为方式。因此,像 MMC_SWITCH 这样的操作码是 而不是 API.

的一部分

据我所知,您应该从相关的 MMC 标准中获取操作码。

(这并不是将这些符号保留在 user-space API 之外的好理由;复制内核 header 比手动转录标准中的值容易得多.而且内核实际上有一个特例来通过这个ioctl来处理EXT_CSD_SANITIZE_START。)