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

Linux字符设备驱动写入设备文件报Invalid argument错误排查

问题现象

开发Linux字符设备驱动时,cat读取设备的功能运行正常,但向设备节点写入数据时直接返回Invalid argument错误,错误截图如下:
写入无效参数错误截图

当前使用的驱动代码如下,未定位到write逻辑的问题:

#include "device_file.h"
#include <linux/fs.h>       /* file stuff */
#include <linux/kernel.h>   /* printk() */
#include <linux/errno.h>    /* error codes */
#include <linux/module.h>   /* THIS_MODULE */
#include <linux/cdev.h>     /* char device stuff */
#include <linux/uaccess.h>  /* copy_to_user() */

static const char g_s_Hello_World_string[] = "Hello from Job & Arief!\n\0";
static const ssize_t g_s_Hello_World_size = sizeof(g_s_Hello_World_string);
static char memory_buffer[500];

/*===============================================================================================*/
static ssize_t device_file_read(struct file *file_ptr, char __user *user_buffer, size_t count, loff_t *possition){

    printk( KERN_NOTICE "Kurniawan_Meulenbeld-driver: Device file is read at offset = %i, read bytes count = %u\n"
    , (int)*possition
    , (unsigned int)count );

    if( *possition >= g_s_Hello_World_size )
    return 0;

    if( *possition + count > g_s_Hello_World_size )
    count = g_s_Hello_World_size - *possition;

    if( copy_to_user(user_buffer, g_s_Hello_World_string + *possition, count) != 0 )
    return -EFAULT;

    *possition += count;
    return count;
}

/*===============================================================================================*/
static ssize_t memory_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos) {
    const char *tmp;
    tmp=buf+count-1;

    if( copy_from_user(memory_buffer,tmp,1) != 0 )
        return -EFAULT;

    return count;
}
/*===============================================================================================*/
static struct file_operations Kurniawan_Meulenbeld_driver_fops =
        {
                .owner = THIS_MODULE,
                .read = device_file_read,
                .write =  memory_write,
        };

static int device_file_major_number = 0;
static const char device_name[] = "Kurniawan_Meulenbeld-driver";

/*===============================================================================================*/
int register_device(void)
{
    int result = 0;

    printk( KERN_NOTICE "Kurniawan_Meulenbeld-driver: register_device() is called.\n" );

    result = register_chrdev( 0, device_name, &Kurniawan_Meulenbeld_driver_fops );
    if( result < 0 )
    {
        printk( KERN_WARNING "Kurniawan_Meulenbeld-driver:  can\'t register character device with errorcode = %i\n", result );
        return result;
    }

    device_file_major_number = result;
    printk( KERN_NOTICE "Kurniawan_Meulenbeld-driver: registered character device with major number = %i and minor numbers 0...255\n"
            , device_file_major_number );

    return 0;
}

/*===============================================================================================*/
void unregister_device(void)
{
    printk( KERN_NOTICE "Kurniawan_Meulenbeld-driver: unregister_device() is called\n" );
    if(device_file_major_number != 0)
    {
        unregister_chrdev(device_file_major_number, device_name);
    }
}
故障原因

报错的核心原因有两点:

  • write回调函数的用户态缓冲区参数buf缺少__user地址空间修饰符。内核会对传入的用户态指针做合法性校验,没有__user标记的指针会被判定为非法地址,直接返回-EINVAL也就是无效参数错误,这是触发报错的直接诱因。
  • 现有写入逻辑本身存在严重缺陷:仅计算了用户缓冲区最后1字节的地址,只尝试拷贝1字节数据到内核缓冲区,既没有校验写入长度防止memory_buffer溢出,也没有更新文件偏移指针f_pos,就算跳过地址校验也无法实现正常写入全量数据的需求。
修复方法

将原有memory_write函数替换为如下实现即可:

static ssize_t memory_write( struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
    size_t free_space;
    // 计算缓冲区剩余可写空间,防止溢出
    free_space = sizeof(memory_buffer) - *f_pos;
    if (free_space == 0) {
        return -ENOSPC;
    }
    // 写入长度超出剩余空间时自动截断
    if (count > free_space) {
        count = free_space;
    }
    // 从用户态拷贝全量待写入数据到内核缓冲区对应偏移位置
    if (copy_from_user(memory_buffer + *f_pos, buf, count) != 0) {
        return -EFAULT;
    }
    // 更新文件偏移
    *f_pos += count;
    // 打印写入内容到内核日志,方便查看
    printk(KERN_NOTICE "Kurniawan_Meulenbeld-driver: write %zu bytes, content: %.*s\n", 
        count, (int)count, memory_buffer + (*f_pos - count));
    return count;
}

替换后重新编译加载驱动,即可正常通过echo等命令向设备节点写入数据,写入内容会直接打印在内核日志中,可通过dmesg查看。

内容的提问来源于stack exchange,提问作者J.Meulenbeld

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:18:16