Правильный способ обработки потоков в ядре?

Я видел фрагменты разбросанной информации, но, похоже, я не могу получить окончательный ответ. Как вы очищаете зомби-поток в ядре?

Чтобы убедиться, и вы получите окончательный правильный способ обработки потоков в ядре, я хотел бы задать этот вопрос более широко. Как вы создаете, завершаете и очищаете поток в ядре Linux?

Что я до сих пор знаю:

thread_func:
    exited = 0;
    while (!must_exit)
        do stuff
    exited = 1;
    do_exit(0)

init_module:
    must_exit = 0;
    exited = 1;
    kthread_run(thread_func, ...)    /* creates and runs the thread */

cleanup_module:
    must_exit = 1;
    while (!exited)
        set_current_state(TASK_INTERRUPTIBLE);
        msleep(1);
    /* How do I cleanup? */

Ближайшая вещь, которую я нашел для решения очистки, - release_task, но я нигде не говорил об этом. Я представил себе, поскольку функции потока kthread_create, kthread_run и т.д., Должны быть kthread_join или kthread_wait, но не было. do_wait также показался вероятным, но он не принимает struct task_struct *.

Кроме того, я не уверен, что do_exit является хорошей идеей или, если она вообще необходима. Может ли кто-нибудь придумать минимальный очерк о том, как создать, прекратить и очистить kthread?

Ответ 1

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

Вам не нужно вызывать do_exit, и если вы собираетесь kthread_stop его из функции выхода из модуля, вы, вероятно, не должны.

Вы можете увидеть это, посмотрев документацию для kthread_create_on_node в kernel/kthread.c (извлечение из ядра Linux 3.3.1):

/**
 * kthread_create_on_node - создать kthread.
 * @threadfn: функция для запуска до signal_pending (текущий).
 * @data: данные ptr для @threadfn.
 * @node: номер памяти node.
 * @namefmt: имя файла printf для потока.
 *
 * Описание: эта вспомогательная функция создает и называет ядро ​​
 * нить. Поток будет остановлен: используйте wake_up_process(), чтобы начать работу  * Это. См. Также kthread_run().
 *
 * Если поток будет привязан к определенному процессору, дайте его node
 * в @node, чтобы получить NUMA-сродство к стекю kthread, или дать -1.
 * Когда woken, поток будет запускать @threadfn() с @data как его  * аргумент. @threadfn() может либо вызвать do_exit() напрямую, если это
 * автономный поток, для которого никто не будет называть kthread_stop(), или
 * return, когда 'kthread_should_stop()' истинно (что означает  * kthread_stop() был вызван). Возвращаемое значение должно быть равно нулю  * или отрицательный номер ошибки; он будет передан kthread_stop().
 *
 * Возвращает task_struct или ERR_PTR (-ENOMEM).
 */

Для kthread_stop присутствует комментарий "сопоставление":

Если threadfn() может вызывать do_exit(), , вызывающий должен убедиться, что task_struct не может уйти.

(И я не уверен, как вы это делаете - возможно, держась за struct_task с get_task_struct.)

Если вы пройдете путь создания потока, вы получите что-то вроде:

kthread_create                                           // macro in kthread.h
  -> kthread_create_on_node                              // in kthead.c
    -> adds your thread request to kthread_create_list
    -> wakes up the kthreadd_task

kthreadd_task устанавливается в init/main.c в reset_init. Он выполняет функцию kthreadd (от kthread.c)

kthreadd                                                 // all in kthread.c
  -> create_kthread
    -> kernel_thread(kthread, your_kthread_create_info, ...)

И сама функция kthread делает:

kthread
  -> initialization stuff
  -> schedule() // allows you to cancel the thread before it actually started
  -> if (!should_stop)
    -> ret = your_thread_function()
  -> do_exit(ret)

... Итак, если your_thread_function просто возвращается, do_exit будет вызываться с его возвращаемым значением. Не нужно делать это самостоятельно.