关于 linux 设备驱动程序第 3 版中 scull_follow 函数的问题

Questions on scull_follow function in linux device drivers 3rd edition

我从未在书中找到 scull_follow 的定义,因此我试图根据 github 存储库 (https://github.com/martinezjavier/ldd3) 来理解它。 这是我试图理解的代码:

struct scull_qset *scull_follow(struct scull_dev *dev, int n) {
  struct scull_qset *qs = dev->data;

  /* Allocate first qset explicitly if need be */
  if (!qs) { // if NULL
    qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
    if (qs == NULL)
      return NULL;  /* Never mind */
    memset(qs, 0, sizeof(struct scull_qset));
  }

  /* Then follow the list */
  while (n--) {
    if (!qs->next) {
      qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
      if (qs->next == NULL)
        return NULL;  /* Never mind */
      memset(qs->next, 0, sizeof(struct scull_qset));
    }
    qs = qs->next;
    continue;
  }
  return qs;
}

这里是结构 scull_qset:

struct scull_qset {
  void **data;
  struct scull_qset *next;
};

从概念上讲,我理解 scull_follow 所做的只是跟随列表直到正确的位置,以便您知道从哪里开始 reading/writing。 我主要对这部分代码感到困惑。

 /* Allocate first qset explicitly if need be */
  if (!qs) { // if NULL
    qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
    if (qs == NULL)
      return NULL;  /* Never mind */
    memset(qs, 0, sizeof(struct scull_qset));
  }

假设用户打开此驱动程序并尝试先从中读取而不是写入。那应该意味着它应该进入 if 语句并分配一些内存。那么,为什么要再次检查 qs 是否为 NULL?它不会因为分配了一些内存而永远不是 NULL 吗?

之后 memset 函数是做什么用的?我知道它将 0 复制到 qs,但是除了初始化这个内存区域之外还有什么意义呢?是这样吗,当你在你的读取函数中调用 copy_to_user 函数时,它会知道因为它填充了 0,所以没有任何 'value' 被写入它所以你只会得到一个空白输出阅读时,假设你做的第一个操作是阅读?

感谢您回答我的问题。

这个:

qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs == NULL)
    return NULL;

是标准 C 编程的好习惯:每当函数失败时,您始终需要检查 return 值。对于任何可能失败的函数都是如此,不仅是 malloc() 和朋友。在这种情况下,kmalloc() 可能无法分配内存,returning NULL,因此代码正在检查该错误。如果发生这种情况,该函数会通过 return NULL; 安全地中止执行,然后调用者将根据需要进行处理。

这个:

memset(qs, 0, sizeof(struct scull_qset));

是标准内核编程的良好做法:无论何时分配未初始化的内存(如 kmalloc() 所做的),它都可能包含敏感的内核数据。您从不 希望未初始化的数据通过 copy_to_user() 或类似的调用到达用户空间。为了避免这种情况,您需要确保在将其提供给用户空间之前对其进行初始化。使用 memset() 用零填充它是最简单的方法之一。

如果用户程序执行 read 作为 scull 驱动程序的第一个系统调用,它只会读取一堆 0 字节。