【问题标题】:Proper way of handling threads in kernel?在内核中处理线程的正确方法?
【发布时间】:2012-04-27 23:46:17
【问题描述】:

我已经看到了一些零散的信息,但我似乎无法得到一个最终答案。如何清理内核中的僵尸线程?

只是为了确保并产生处理内核中线程的最终正确方法,我想更广泛地提出这个问题。 如何在 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_createkthread_run等,应该有kthread_joinkthread_wait,但没有。 do_wait 似乎也有可能,但不需要struct task_struct *

此外,我不确定do_exit 是否是个好主意,或者是否有必要。有人可以提出如何创建、终止和清理 kthread 的最小草图吗?

【问题讨论】:

  • 我好像记得有一个kthread_stop,或者kthread_should_stop,类似的东西。
  • @MartinJames,按照我的理解,你要么退出自己(使用do_exit),要么轮询kthread_should_stop,直到有人(cleanup_module)调用kthread_stop。我没有找到任何地方说kthread_stop 是否也清理了线程。让我想知道的是,如果人们(在互联网上)建议使用do_exit 或其他什么,难道不应该有办法在do_exit 之后清理线程吗?
  • 顺便说一句,this 是我说我无法得出结论性答案时所说的。那里有很多相互矛盾的东西。

标签: c multithreading linux-kernel zombie-process


【解决方案1】:

执行此操作的“正确”方法之一是让您的线程函数检查它是否为kthread_should_stop,如果确实需要停止则直接返回。

你不需要调用do_exit,如果你打算从模块退出函数中调用kthread_stop,你可能不应该。

您可以通过查看kernel/kthread.c 中的kthread_create_on_node 文档来了解这一点(从 Linux 内核 3.3.1 中提取):

/**
* kthread_create_on_node - 创建一个 kthread。
* @threadfn:在 signal_pending(current) 之前运行的函数。
* @data:@threadfn 的数据指针。
* @node: 内存节点号。
* @namefmt:线程的 printf 样式名称。
*
* 说明:此辅助函数创建并命名内核
* 线。线程将被停止:使用 wake_up_process() 启动
* 它。另请参见 kthread_run()。
*
* 如果线程要绑定在特定的 cpu 上,则给出它的节点
* 在@node 中,获取 kthread 堆栈的 NUMA 亲和性,否则给出 -1。
* 当被唤醒时,线程将运行@threadfn() 并以@data 作为它的
* 争论。 @threadfn() 可以直接调用 do_exit() 如果它是一个
* 没有人会调用 kthread_stop() 的独立线程,或者
* 当 'kthread_should_stop()' 为真时返回(这意味着
* kthread_stop() 已被调用)。返回值应为零
* 或负错误号;它将被传递给 kthread_stop()。
*
* 返回一个 task_struct 或 ERR_PTR(-ENOMEM)。
*/

kthread_stop 存在“匹配”评论:

如果 threadfn() 可以自己调用 do_exit(),调用者必须确保 task_struct 不能消失

(我不确定你是怎么做到的 - 可能会用get_task_struct 保持struct_task。)

如果你走线程创建的路径,你会得到类似的东西:

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_taskinit/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's actually started
  -> if (!should_stop)
    -> ret = your_thread_function()
  -> do_exit(ret)

... 所以如果your_thread_function 简单地返回,do_exit 将以其返回值被调用。不需要自己做。

【讨论】:

  • 嗯,任务结构是一个全局变量,所以它不能去任何地方。但是,这是否意味着如果独立线程调用do_exit()(因此不应该调用kthread_stop)就不需要清理?
  • 它可以去很多地方。如果该任务结构所引用的任务已完全完成,并且该任务结构被退出路径释放,那么您在模块数据中拥有的副本就像一个悬空指针 - 您无法使用它。
  • 是的,如果您不打算 kthread_stop 您的线程,它可以调用 do_exit 并且会进行正常清理。 但是如果你的线程设法超过你的模块,你就有麻烦了。
  • 好的,如果清理完成了,那就没问题了。我已确保在cleanup_module 中等待线程返回。我已经被它咬了。
  • 只是为了确保,kthread_stop 也会等待线程完成。这是一个非常漂亮的包装。但我想它可能不太适合您的用例。
猜你喜欢
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-28
  • 1970-01-01
  • 1970-01-01
  • 2013-07-16
  • 1970-01-01
相关资源
最近更新 更多