Вопросы по функции scull_follow в linux драйверов устройств 3-й редакции - PullRequest
0 голосов
/ 10 июля 2020

Я так и не нашел определения 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;
}

Вот struct scull_qset:

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

Концептуально я понимаю, что все, что делает scull_follow, это то, что он следует список до нужной позиции, чтобы вы знали, с чего начать чтение / запись. В основном меня смущает эта часть кода.

 /* 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));
  }

Допустим, пользователь открывает этот драйвер и пытается сначала прочитать его, не записывая. это должно означать, что он должен go в операторе if и выделить некоторую память. Тогда почему есть вторая проверка, является ли qs NULL или нет? Разве он всегда не будет NULL, потому что ему была выделена некоторая память?

После этого для чего нужна функция memset? Я понимаю, что он копирует 0 в qs, но какой в ​​этом смысл, кроме инициализации этой области памяти? Это так, что когда вы вызываете функцию copy_to_user в своей функции чтения, она будет знать, что, поскольку она заполнена нулями, в нее не было записано ничего из 'значения', поэтому вы просто получите пустой вывод при чтении, предполагая первая операция, которую вы выполните, это чтение?

Спасибо за ответ на мои вопросы.

1 Ответ

1 голос
/ 11 июля 2020

Это:

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

Стандартное C хорошая практика программирования: всякий раз, когда функция может дать сбой, вам всегда нужно проверять возвращаемое значение. Это верно для любой функции, которая может дать сбой, не только для malloc() и других. В этом случае kmalloc() может не выделить память, возвращая NULL, поэтому код проверяет наличие этой ошибки. Если это произойдет, функция безопасно прервет выполнение, выполнив return NULL;, и вызывающий обработает это по мере необходимости.

Это:

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

Это стандартная хорошая практика программирования ядра: всякий раз, когда вы выделяете неинициализированную память (например, kmalloc()), она может содержать конфиденциальные данные ядра. Вы никогда не хотите, чтобы неинициализированные данные доходили до пользовательского пространства через copy_to_user() или аналогичные вызовы. Чтобы этого избежать, вам необходимо инициализировать его перед тем, как сделать его доступным для пользовательского пространства. Заполнение его нулями с помощью memset() - один из самых простых способов сделать это.

В случае, если пользовательская программа выполняет read в качестве первого системного вызова в драйвере scull, она просто прочитает кучу из 0 байт.

...