ioctl cmd 不能等于 2 的小问题

平时很少写内核模块,今天遇到一个小问题,写了一个内核模块,想在用户态通过 ioctl与该模块进行交互,于是在内核模块和用户态程序中定义了相同的枚举变量,如下所示:

enum {
   CMD_1,
   CMD_2,
   CMD_3,
   CMD_4,
};

很明显,CMD_3等于 2, 但在调用 ioctl(fd, CMD_3, …)后,发现该请求一直没有到达内核模块,折腾了好长时间才知道 ioctlcmd不能随意指定,比如 2就是一个特殊的值。在 ioctl请求到达内核模块之前,内核中的其他代码还进行了处理,如下所示(4.19内核):

int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
         unsigned long arg)
{
    int error = 0;
    int __user *argp = (int __user *)arg;
    struct inode *inode = file_inode(filp);

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

    case FIONCLEX:
        set_close_on_exec(fd, 0);
        break;

    case FIONBIO:
        error = ioctl_fionbio(filp, argp);
        break;

    case FIOASYNC:
        error = ioctl_fioasync(fd, filp, argp);
        break;

    case FIOQSIZE:
        if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) ||
            S_ISLNK(inode->i_mode)) {
            loff_t res = inode_get_bytes(inode);
            error = copy_to_user(argp, &res, sizeof(res)) ?
                    -EFAULT : 0;
        } else
            error = -ENOTTY;
        break;

    case FIFREEZE:
        error = ioctl_fsfreeze(filp);
        break;

    case FITHAW:
        error = ioctl_fsthaw(filp);
        break;

    case FS_IOC_FIEMAP:
        return ioctl_fiemap(filp, arg);

    case FIGETBSZ:
        /* anon_bdev filesystems may not have a block size */
        if (!inode->i_sb->s_blocksize)
            return -EINVAL;
        return put_user(inode->i_sb->s_blocksize, argp);

    case FICLONE:
        return ioctl_file_clone(filp, arg, 0, 0, 0);

    case FICLONERANGE:
        return ioctl_file_clone_range(filp, argp);

    case FIDEDUPERANGE:
        return ioctl_file_dedupe_range(filp, argp);

    default:
        if (S_ISREG(inode->i_mode))
            error = file_ioctl(filp, cmd, arg);
        else
            error = vfs_ioctl(filp, cmd, arg);
        break;
    }
    return error;
}

在上面的代码中,进入内核模块的函数是 file_ioctl()vfs_ioctl(),但由于 FIGETBSZ的值等于 2,因此当传入的 cmd等于 2时,就无法进入内核模块。具体可以参考这个问题:ioctl is not called if cmd 2

解决办法是使用 ioctl提供的宏(比如_IO_IOR_IOW, _OIWR)来定义 cmd。典型的用法如 KVM提供给用户态程序(比如 QEMU)的 ioctl cmd,如下所示:

#define KVMIO 0xAE

#define KVM_CREATE_VCPU           _IO(KVMIO,   0x41)
#define KVM_RUN                   _IO(KVMIO,   0x80)
#define KVM_GET_REGS              _IOR(KVMIO,  0x81, struct kvm_regs)
#define KVM_SET_REGS              _IOW(KVMIO,  0x82, struct kvm_regs)
#define KVM_CREATE_DEVICE         _IOWR(KVMIO,  0xe0, struct kvm_create_device)

简单场景的话,可以像下面这样使用:

#define CMD_1 _IO(’X', 0)
#define CMD_2 _IO(‘X', 1)
#define CMD_3 _IO(‘X', 2)
#define CMD_4 _IO(‘X', 3)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值