kthread_stop导致内核崩溃

3

我正在尝试学习自旋锁和内核线程,并编写了一个小模块来测试我的内核代码理解。以下是代码片段:

static int kernel_test_thread(void *__unused) {
    int work;
    int x;
    allow_signal(SIGKILL);
    spin_lock(&kernel_test_device_lock);
    while(current->vfork_done == NULL || !kthread_should_stop())
    {
        if(signal_pending( current ))
            break;

        spin_unlock(&kernel_test_device_lock);
        msleep_interruptible(100);
        spin_lock(&kernel_test_device_lock);

        //do some work here
        for(work=0;work<=10000;++work)
        {
            x = work<<1;
        }
    }
    spin_unlock(&kernel_test_device_lock);
    return 0;
}

static int __init start_kernel_test(void) 
{
struct task_struct * ptask;
ptask = kthread_run(kernel_test_thread, NULL, "kernel_test_thread");
if(IS_ERR(ptask))
    return -1;

kernel_test_task = ptask;
return 0;
}

static void stop_kernel_test(void)
{

if(kernel_test_task)
    kthread_stop(kernel_test_task);
kernel_test_task=0;
}

static int __init init_test_kernel(void)
{
int rv;


spin_lock_init(&kernel_test_device_lock);
rv = start_kernel_test();
if(rv)
    return rv;

printk(KERN_INFO "kernel_test: Kernel Test module started\n");
return 0;
}

// Cleanup module
static void __exit cleanup_test_kernel(void)
{
spin_lock_bh(&kernel_test_device_lock);
stop_kernel_test();
spin_unlock_bh(&kernel_test_device_lock);
printk(KERN_INFO "kernel_test: Kernel Test module stopped\n");
}

module_init(init_test_kernel);
module_exit(cleanup_test_kernel);

当我尝试移除该模块时,在“/var/log/syslog”中会得到以下堆栈转储信息。

Jun 15 10:42:31 manik kernel: [  595.162463] BUG: scheduling while atomic:   rmmod/1719/0x00000200
Jun 15 10:42:31 manik kernel: [  595.162470] Modules linked in:  kernel_test(OE-) intel_rapl x86_pkg_temp_thermal intel_powerclamp coretemp  kvm_intel kvm irqbypass crc32_pclmul snd_usb_audio lpc_ich snd_usbmidi_lib input_leds joydev hid_multitouch snd_hda_codec_hdmi snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep ie31200_edac shpchp edac_core 8250_fintek snd_soc_rt5640 snd_soc_rl6231 snd_soc_core snd_compress ac97_bus snd_pcm_dmaengine snd_pcm snd_seq_midi snd_seq_midi_event snd_rawmidi snd_seq snd_seq_device snd_timer dw_dmac snd dw_dmac_core elan_i2c snd_soc_sst_acpi spi_pxa2xx_platform soundcore 8250_dw i2c_designware_platform i2c_designware_core soc_button_array mac_hid parport_pc ppdev lp parport autofs4 nouveau i915 mxm_wmi wmi ttm i2c_algo_bit drm_kms_helper e1000e syscopyarea ptp ahci sysfillrect libahci sysimgblt fb_sys_fops pps_core drm sdhci_acpi video sdhci i2c_hid fjes hid_generic usbhid hid
Jun 15 10:42:31 manik kernel: [  595.162568] CPU: 3 PID: 1719 Comm: rmmod Tainted: G          IOE   4.4.0-22-generic #40
Jun 15 10:42:31 manik kernel: [  595.162572] Hardware name: ADLINK Technology Inc. Express-HL./SHARKBAY, BIOS 1.14 01/01/2013
Jun 15 10:42:31 manik kernel: [  595.162575]  c1ac1967 b70efc6e 00000286 eeebde14 c139dccf e7d44dc0 c1c64dc0 eeebde2c
Jun 15 10:42:31 manik kernel: [  595.162584]  c1090627 c19b6c68 e5343ff0 000006b7 00000200 eeebde68 c17a4518 ffffffff
Jun 15 10:42:31 manik kernel: [  595.162593]  e7d0be00 b70efc6e e7d0be00 00000003 e7d0be00 00000000 c10a4760 e7d44dc0
Jun 15 10:42:31 manik kernel: [  595.162601] Call Trace:
Jun 15 10:42:31 manik kernel: [  595.162615]  [<c139dccf>] dump_stack+0x58/0x79
Jun 15 10:42:31 manik kernel: [  595.162623]  [<c1090627>] __schedule_bug+0x57/0x70
Jun 15 10:42:31 manik kernel: [  595.162630]  [<c17a4518>] __schedule+0x5e8/0x770
Jun 15 10:42:31 manik kernel: [  595.162637]  [<c10a4760>] ? enqueue_task_fair+0x90/0xd40
Jun 15 10:42:31 manik kernel: [  595.162642]  [<c17a46cd>] schedule+0x2d/0x80
Jun 15 10:42:31 manik kernel: [  595.162648]  [<c17a7085>] schedule_timeout+0x185/0x210
Jun 15 10:42:31 manik kernel: [  595.162655]  [<c124c8b1>] ? sysfs_kf_seq_show+0xb1/0x150
Jun 15 10:42:31 manik kernel: [  595.162661]  [<c1095d0d>] ? check_preempt_curr+0x4d/0x90
Jun 15 10:42:31 manik kernel: [  595.162666]  [<c1095d67>] ? ttwu_do_wakeup+0x17/0x110
Jun 15 10:42:31 manik kernel: [  595.162672]  [<c17a4fd2>] wait_for_completion+0x92/0xf0
Jun 15 10:42:31 manik kernel: [  595.162678]  [<c1096c00>] ? wake_up_q+0x70/0x70
Jun 15 10:42:31 manik kernel: [  595.162684]  [<c108b771>] kthread_stop+0x41/0xf0
Jun 15 10:42:31 manik kernel: [  595.162691]  [<f06c109b>] cleanup_test_kernel+0x1b/0xf80 [kernel_test]
Jun 15 10:42:31 manik kernel: [  595.162698]  [<c10f4a0c>] SyS_delete_module+0x1ac/0x200
Jun 15 10:42:31 manik kernel: [  595.162704]  [<c11dc7bd>] ? ____fput+0xd/0x10
Jun 15 10:42:31 manik kernel: [  595.162709]  [<c1089a64>] ? task_work_run+0x84/0xa0
Jun 15 10:42:31 manik kernel: [  595.162715]  [<c10030f6>] ? exit_to_usermode_loop+0xb6/0xe0
Jun 15 10:42:31 manik kernel: [  595.162721]  [<c100393d>] do_fast_syscall_32+0x8d/0x150
Jun 15 10:42:31 manik kernel: [  595.162728]  [<c17a8098>] sysenter_past_esp+0x3d/0x61

请帮我理解这里到底发生了什么?

谢谢


你确定可以在原子上下文中调用 kthread_should_stop() 吗?好的,答案是肯定的。 - 0andriy
1
罪魁祸首是在 kthread_stop() 之前的 spin_lock_bh() - 0andriy
@AndyShevchenko 我认为这就是我们应该检查线程是否被要求停止执行的方式。 - Monku
@AndyShevchenko 你能解释一下为什么吗? - Monku
这在你的回溯中已经写明了。我之前没有注意到这一点。我确实走了更长的路,即阅读代码(内核核心代码)。 - 0andriy
@AndyShevchenko,抱歉,能否请您解释一下您的答案?谢谢。 - Monku
1个回答

4

spin_lock_bh()开始一个原子操作区域,在此期间禁止调用任何可能等待的函数。但是kthread_stop()等待线程退出。

这是因为从kthread退出会销毁线程结构,除非有人在此之前增加了线程的使用计数器。当调用kthread_stop()时,它会:

  1. 增加kthread的使用计数器。
  2. 设置kthread的“停止”标志。
  3. 等待kthread完成。
  4. 减少kthread的使用计数器。

kthread_stop的这种算法保证了:

在发现kthread_should_stop()不为零后退出kthread始终是安全的。

但是,如果kthread可能在未检查kthread_should_stop()的情况下退出,则应采取额外的措施来保证kthread_stop()不会看到kthread被销毁。可能的方法是:

struct task_struct* kernel_test_task;

int module_init(void)
{
    // Create kthread, but don't start it.
    kernel_test_task = kthread_create(...);
    // Increments usage counter.
    get_task_struct(kernel_test_task);
    // Now it is safe to start kthread - exiting from it doesn't destroy its struct.
    wake_up_process(kernel_test_task);
}

void module_cleanup(void)
{
    // While thread may be finished now, its structure is garanteed to be alive.
    kthread_stop(kernel_test_task);
    // This will decrement usage counter, incremented in module_init.
    put_task_struct(kernel_test_task);
    // Now thread is garanteed to be finished, and its struct destroyed.
}

我认为在“cleanup_test_kernel”函数中,我并不真正需要使用“spin_lock_bh”或任何其他自旋锁。如果“kthread_stop”将等待线程退出,那么当“kthread_stop”返回时,内核线程确实已经退出了。 - Monku
2
唯一的问题是kthread_stop可能在kthread已经退出和销毁时被调用。但在示例代码中不需要担心这个问题。 - Tsyvarev
这种情况仍然可能发生。如果“kernel_test_thread”线程接收到信号并退出,那么我将在已经退出的线程上调用“kthread_stop”。那么会发生什么? - Monku
1
如果对已经退出并且其结构已被销毁的线程调用 kthread_stop,可能会发生糟糕的事情。我已经更新了我的答案,介绍了避免这种情况的方法。 - Tsyvarev
1
Tsyvarev - 我喜欢你的答案,因此我已接受它。谢谢你。 - Monku
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接