You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

块设备驱动:解析收到的ioctl及虚拟块设备异常行为

嘿,我来帮你梳理下这个虚拟块设备的ioctl异常问题。你已经搞定了读写操作的并发保护(多读者单写者的模型很标准),但没处理ioctl就出现异常——这其实是块设备驱动的常见坑,因为用户空间的工具(比如磁盘工具、文件系统挂载工具)会默认给块设备发一堆ioctl请求来获取设备信息,没处理的话很容易触发意料之外的行为。

先搞清楚到底是哪些ioctl在搞事情

既然你已经在记录日志,那先给驱动加个最基础的ioctl入口,把收到的命令码打出来,这样就能定位异常来源:

#include <linux/fs.h>

long your_vdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    printk(KERN_INFO "[vdev] Received ioctl cmd: 0x%x\n", cmd);
    // 默认返回ENOTTY,告诉用户空间该命令不被支持
    return -ENOTTY;
}

然后别忘了在你的file_operations结构体里注册这个函数:

static const struct file_operations vdev_fops = {
    .owner = THIS_MODULE,
    .read = vdev_read,
    .write = vdev_write,
    .unlocked_ioctl = your_vdev_ioctl,
    .compat_ioctl = your_vdev_ioctl, // 兼容32位用户空间的话加上
    // 其他已实现的操作...
};

重启驱动测试后,你就能在dmesg里看到具体的命令码——大概率是块设备常用的BLKSSZGET(获取扇区大小)、BLKGETSIZE(总扇区数)这类基础查询命令。

先实现几个必备的ioctl避免基础异常

哪怕你不需要复杂的ioctl功能,也得把块设备的基础信息查询命令实现了,否则很多用户空间工具会报错甚至崩溃。这里给你个示例:

#include <linux/blkdev.h>

long your_vdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct vdev_dev *dev = filp->private_data; // 你的设备结构体指针
    int ret = 0;

    switch(cmd) {
        // 返回逻辑扇区大小(你的设备是512字节)
        case BLKSSZGET:
            ret = put_user(512, (int __user *)arg);
            break;
        // 返回总扇区数(你的设备是2个扇区)
        case BLKGETSIZE:
            ret = put_user((unsigned long)2, (unsigned long __user *)arg);
            break;
        // 返回总字节数(2*512=1024)
        case BLKGETSIZE64:
            ret = put_user((u64)1024, (u64 __user *)arg);
            break;
        default:
            printk(KERN_INFO "[vdev] Unsupported ioctl cmd: 0x%x\n", cmd);
            ret = -ENOTTY;
            break;
    }
    return ret;
}
别忘给ioctl加上并发保护

你已经给读写加了锁,但ioctl操作如果涉及设备状态修改(比如以后加个重置设备的命令),必须和读写操作互斥。比如如果是修改类ioctl,要加写锁;如果只是查询类,加读锁就行:

// 假设你的设备结构体里有读写锁:struct rw_semaphore rw_lock;
case VDEV_RESET: // 自定义的重置设备命令
    if (down_write_interruptible(&dev->rw_lock)) {
        return -ERESTARTSYS;
    }
    memset(dev->mem_region, 0, 1024); // 清空内存区域
    up_write(&dev->rw_lock);
    return 0;
case VDEV_GET_INFO: // 查询类命令
    down_read(&dev->rw_lock);
    // 读取设备信息并拷贝到用户空间
    up_read(&dev->rw_lock);
    return 0;
额外注意:用户空间参数的合法性检查

如果ioctl需要传递用户空间的指针,一定要用access_ok检查指针的合法性,否则容易触发内核Oops:

case VDEV_SET_PARAM:
    if (!access_ok((void __user *)arg, sizeof(struct vdev_param))) {
        return -EFAULT;
    }
    // 后续处理参数拷贝
    break;

内容的提问来源于stack exchange,提问作者Arkaik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:28:10