【问题标题】:linux get_user function unexpected behaviourlinux get_user 函数意外行为
【发布时间】:2020-03-17 09:38:18
【问题描述】:

我正在尝试为 linux 内核实现一个单字节字符设备驱动程序,作为我的操作系统模块分配的一部分。要实现设备驱动程序,我必须定义读写函数。下面是我的设备写函数

ssize_t onebyte_write(struct file *filep, const char *buf, size_t count, loff_t *f_pos)
{
    if (get_user(*onebyte_data, buf) != 0)
        return 0;
    if (count > 1)
        printk(KERN_ALERT "Error: No space left on device\n");
    return 1;
}

'onebyte_data' 是一个 char 指针,指向内核空间中动态分配的一字节内存。据我了解,“get_user”应该将一个简单的变量(int 或 char)从用户空间复制到内核空间。但是当我执行以下一组命令时

printf abc > /dev/onebyte
cat /dev/onebyte

结果是 'c' 而不是 'a',这意味着 get_user 已读取整个输入字符串而不仅仅是第一个字符,而是存储最后一个字符 'c'。这是预期的行为吗?如果我只想将输入的第一个字符存储在onebyte_data中该怎么办?

【问题讨论】:

  • 创建一个minimal-reproducible-example。如果您的onebyte_data 属于那里,请进行编辑并显示它。据我了解,您将 int 值传递给需要指针的函数。

标签: c linux-kernel


【解决方案1】:

结果是 'c' 而不是 'a',这意味着 get_user 已读取整个输入字符串...

没有。这意味着您的.write 函数被多次调用。 'c' 是上次调用时写的字符。


大多数写入文件的用户空间实用程序都在高级上执行此操作。他们可能会多次调用.write 函数(通过write syscall)。这取决于.write 函数的返回值

  • n < 0 表示 .write错误 结束。不再调用 .write
  • n = count 表示.write 已处理传递给它的所有字节。不再调用 .write
  • 0 <= n < count 表示 .write 只处理了 first n bytes 传递给它。对 .write 的调用将重复剩余字节。

注意,高级用户空间函数允许将输入字符串分成几个块,并且一次只将一个块传递给.write。在这种情况下,.write 函数将被重复调用,直到它写入最后一个块中的所有字节(或返回错误指示符)。


成功时返回 1,这意味着您的 .write 函数只处理一个单字节。所以函数被调用来处理剩余的字节(count 减 1)。仅当您的 .write 函数在最后一个字节被调用时,才不再进行调用。这是因为返回值 (1) 等于传递给函数的字节数。

如果我只想将输入的第一个字符存储在 onebyte_data 中该怎么办?

第一个字符是偏移量为 0 的字符。因此您只需存储 *f_pos 为 0 时的字符。所有其他字符应忽略,即您需要返回 count .

ssize_t onebyte_write(struct file *filep, const char *buf, size_t count, loff_t *f_pos)
{
    if (*f_pos == 0) {
      // Need to store the first character.
      if (get_user(*onebyte_data, buf) != 0)
          return -EFAULT; // This is how an error should be signalled in `.write` function.
    }

    // We will return `count` for ignore all (other) characters.
    //
    // Before that we need to update `f_pos` parameter
    // to point after the bytes "written" (actually, ignored).
    *f_pos += count;

    return count;
}

由于您的函数总是返回传递给它的字节数,您可以省略更新f_pos 参数:

ssize_t onebyte_write(struct file *filep, const char *buf, size_t count, loff_t *f_pos)
{
    if (*f_pos == 0) {
      // Need to store the first character.
      if (get_user(*onebyte_data, buf) != 0)
          return -EFAULT; // This is how an error should be signalled in `.write` function.
    }
    return count;
}

此类更改不会影响通过echoprintf 写入文件:

# printf abcd > /dev/onebyte
# cat /dev/onebyte
a

这是因为echo 实际上并没有将小的输入字符串拆分成块,因此您的 write 函数只会被调用一次

使用dd 实用程序时情况会发生变化:

# printf abcd | dd bs=2 count=2 of=/dev/onebyte
# cat /dev/onebyte
c

那一次dd 调用.write 函数恰好两次,第一次将ab 传递给它,第二次-cd

因为您在第一次调用后没有更改f_pos,所以第二次调用的f_pos 也将等于0,因此您将存储传递给该调用的第一个字节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-16
    • 1970-01-01
    • 2016-04-16
    • 1970-01-01
    相关资源
    最近更新 更多