ioctl 如何知道在 Linux 中调用哪个函数?

发布于 2024-10-18 08:01:34 字数 51 浏览 5 评论 0原文

那么,当我使用 ioctl 编号在设备上调用 ioctl 时,它如何知道要调用哪个函数?

So when I call an ioctl on a device, with an ioctl number, how does it know which function to call?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

小鸟爱天空丶 2024-10-25 08:01:34

ioctl(2) 通过 fs/ioctl.c 函数进入:

SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
    struct file *filp;
    int error = -EBADF;
    int fput_needed;

    filp = fget_light(fd, &fput_needed);
    if (!filp)
            goto out;

    error = security_file_ioctl(filp, cmd, arg);
    if (error)
            goto out_fput;

    error = do_vfs_ioctl(filp, fd, cmd, arg);
 out_fput:
    fput_light(filp, fput_needed);
 out:
    return error;
}

请注意,已经有一个关联的文件描述符 fd。然后内核调用 fget_light() 来查找 filp (粗略地说,文件指针,但不要将其与标准 IO FILE * 混淆) > 文件指针)。对 security_file_ioctl() 的调用检查加载的安全模块是否允许 ioctl(无论是通过名称,如 AppArmor 和 TOMOYO,还是通过标签,如 SMACK 和 SELinux) ),以及用户是否具有进行呼叫的正确能力(能力(7))。如果允许调用,则调用 do_vfs_ioctl() 来处理常见的 ioctl 本身:

    switch (cmd) {
    case FIOCLEX:
            set_close_on_exec(fd, 1);
            break;
    /* ... */

如果这些常见情况都不正确,则内核调用辅助例程:

static long vfs_ioctl(struct file *filp, unsigned int cmd,
                  unsigned long arg)
{
    int error = -ENOTTY;

    if (!filp->f_op || !filp->f_op->unlocked_ioctl)
            goto out;

    error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
    if (error == -ENOIOCTLCMD)
            error = -EINVAL;
 out:
    return error;
}

驱动程序提供自己的 .unlocked_ioctl 函数指针,如 fs/pipe.c 中的管道实现:

const struct file_operations rdwr_pipefifo_fops = {
    .llseek         = no_llseek,
    .read           = do_sync_read,
    .aio_read       = pipe_read,
    .write          = do_sync_write,
    .aio_write      = pipe_write,
    .poll           = pipe_poll,
    .unlocked_ioctl = pipe_ioctl,
    .open           = pipe_rdwr_open,
    .release        = pipe_rdwr_release,
    .fasync         = pipe_rdwr_fasync,
};

The ioctl(2) enters via the fs/ioctl.c function:

SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
    struct file *filp;
    int error = -EBADF;
    int fput_needed;

    filp = fget_light(fd, &fput_needed);
    if (!filp)
            goto out;

    error = security_file_ioctl(filp, cmd, arg);
    if (error)
            goto out_fput;

    error = do_vfs_ioctl(filp, fd, cmd, arg);
 out_fput:
    fput_light(filp, fput_needed);
 out:
    return error;
}

Note that there is already a filedescriptor fd associated. The kernel then calls fget_light() to look up a filp (roughly, file pointer, but don't confuse this with the standard IO FILE * file pointer). The call into security_file_ioctl() checks whether the loaded security module will allow the ioctl (whether by name, as in AppArmor and TOMOYO, or by labels, as in SMACK and SELinux), as well as whether or not the user has the correct capability (capabilities(7)) to make the call. If the call is allowed, then do_vfs_ioctl() is called to either handle common ioctls itself:

    switch (cmd) {
    case FIOCLEX:
            set_close_on_exec(fd, 1);
            break;
    /* ... */

If none of those common cases are correct, then the kernel calls a helper routine:

static long vfs_ioctl(struct file *filp, unsigned int cmd,
                  unsigned long arg)
{
    int error = -ENOTTY;

    if (!filp->f_op || !filp->f_op->unlocked_ioctl)
            goto out;

    error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
    if (error == -ENOIOCTLCMD)
            error = -EINVAL;
 out:
    return error;
}

Drivers supply their own .unlocked_ioctl function pointer, like this pipe implementation in fs/pipe.c:

const struct file_operations rdwr_pipefifo_fops = {
    .llseek         = no_llseek,
    .read           = do_sync_read,
    .aio_read       = pipe_read,
    .write          = do_sync_write,
    .aio_write      = pipe_write,
    .poll           = pipe_poll,
    .unlocked_ioctl = pipe_ioctl,
    .open           = pipe_rdwr_open,
    .release        = pipe_rdwr_release,
    .fasync         = pipe_rdwr_fasync,
};
送你一个梦 2024-10-25 08:01:34

简单的解释:

传递给 ioctl 的文件描述符指向表示要 ioctl 的设备的 inode 结构。

inode 结构包含设备号dev_t i_rdev,它用作查找设备驱动程序的file_operations 结构的索引。在此结构中,有一个指向设备驱动程序定义的ioctl函数的指针。

您可以阅读 Linux 设备驱动程序,第三版 以获取更详细的说明。它可能有点过时,但仍然是一本好书。

A simplified explanation:

The file descriptor you pass to ioctl points to the inode structure that represents the device you are going to ioctl.

The inode structure contains the device number dev_t i_rdev, which is used as an index to find the device driver's file_operations structure. In this structure, there is a pointer to the ioctl function defined by the device driver.

You can read Linux Device Drivers, 3rd Edition for a more detailed explanation. It may be a bit outdated, but a good read nevertheless.

慈悲佛祖 2024-10-25 08:01:34

内核中有一个映射。如果您编写驱动程序,则可以注册自己的 ioctl 代码。

编辑:我曾经编写过一个 ATA over Ethernet 驱动程序,并实现了一个自定义 ioctl,用于在运行时调整驱动程序。

There's a map in the kernel. You can register your own ioctl codes if you write a driver.

Edit: I wrote an ATA over Ethernet driver once and implemented a custom ioctl for tuning the driver at runtime.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文