Это хорошая практика, чтобы завершить поток ядра с помощью «return» или «do_exit ()»? - PullRequest
6 голосов
/ 10 июня 2019

Моя цель - выполнить поток ядра из функции зонда моего драйвера ТОЛЬКО ОДИН РАЗ, который выполняет загрузку встроенного программного обеспечения.

Для простоты приведен пример кода (не фактический),

#include<linux/module.h>
#include<linux/init.h>
#include<linux/kthread.h>

MODULE_LICENSE("GPL");

struct task_struct *kthread;


static int thread_func(void* data)
{
    printk("In %s function\n", __func__);
    return 0;
}

static int hello_init(void)
{
    int ret = 0;

    printk("Hello World\n");
    kthread = kthread_run(thread_func,
            NULL, "kthread-test");
    if (IS_ERR(kthread)) {
        ret = PTR_ERR(kthread);
        printk("Unable to run kthread err %d\n", ret);
        return ret;
    }
    return 0;
}


static void hello_exit(void)
{
    printk("Bye World\n");

}

Я не использую ничего из следующего, потому что:

  1. kthread_should_stop() - используется для непрерывного выполнения, которое я не хочу
  2. kthread_stop(struct task_struct *thread) - если включено в функцию выхода из модуля, вызывает панику ядра, так как поток уже был завершен после однократного выполнения

Это правильный подход? если нет, пожалуйста, предложите

1 Ответ

6 голосов
/ 10 июня 2019

Необходимо убедиться, что поток завершился, прежде чем функция выхода из модуля вернется.Один из способов сделать это - использовать структуру завершения.

Основная идея состоит в том, чтобы инициализировать структуру завершения перед запуском потока, заставить поток пометить структуру завершения как «завершенную» при выходе изаставить функцию выхода из модуля (или что-то еще) ждать, пока структура завершения будет помечена как завершенная.

  1. Предпосылки

    #include <linux/completion.h>
    
  2. Инициализация структуры завершения

    Если переменная структуры завершения статически размещена, ее можно инициализировать в определении переменной с помощью макроса DECLARE_COMPLETION:

    static DECLARE_COMPLETION(thread_done);
    

    (Существует также макрос DECLARE_COMPLETION_ONSTACK для использования, когда переменная структуры завершения находится в стеке.)

    В качестве альтернативы можно определить неинициализированный struct completion (например, как член динамическивыделенной структуры) и впоследствии инициализируется путем вызова init_completion(...):

    struct completion thread_done;
    
    ...
    
    init_completion(&thread_done);
    
  3. Создать поток

    kthread = kthread_run(thread_func, NULL, "kthread-test");
    if (IS_ERR(kthread)) {
        complete(&thread_done); /* <-- may or may not be required */
        ret = PTR_ERR(kthread);
        return ret;
    }
    

    В приведенном вышеЕсли f kthread_run(...) терпит неудачу, структура завершения помечается как «полная», если какой-либо код ожидает завершения позже.Если гарантируется, что ничто не будет ждать завершения позже, вызов complete(&thread_done); может быть пропущен.

  4. Выход из потока

    Вместовозвращаясь из функции потока или вызывая do_exit(...), поток должен вызвать complete_and_exit(...), чтобы пометить поток как «завершенный»:

    complete_and_exit(&thread_done, 0);
    

    Вызов complete_and_exit(...) безопаснее, чем отдельные вызовы complete(...) иdo_exit(...).При отдельных вызовах complete(...) и do_exit(...) существует вероятность того, что код модуля уже будет выгружен при возврате complete(...), поэтому поток может выполнить несуществующий или случайный код.Вызов complete_and_exit(...) позволяет избежать этого, потому что функция существует вне кода модуля и никогда не возвращается.

  5. Убедитесь, что поток завершен

    Для обеспечениячто поток завершен, вызовите wait_for_completion(...):

    wait_for_completion(&thread_done);
    

    По возвращении поток либо уже завершился, либо все еще выполняется в вызове complete_and_exit(...) и собирается завершить работу.В любом случае, он больше не выполняет какой-либо код модуля, поэтому его можно продолжить.

...