为什么不能在中断上半部休眠?

这是一个老生常谈的问题。

我们先简单说下什么是中断「因为最近在群里看到有人竟然不懂什么是中断」。中断是计算机里面非常核心的东西,我们可以跑OS,可以多任务运行都因为中断的存在。

假设你是一个CPU,你正在睡觉。

你突然觉得肚子疼,「这个事情就是一个中断你睡觉的事情」,然后你就去上厕所了。

然后你又回到床上睡觉。

你突然又觉得肚子饿了,「这个事情也是一个中断你睡觉的事情」,然后你就去吃了个汉堡。

中断可以认为是突发的事情,并且时间不是特别长,如果时间非常久,那就不能叫中断了,就叫主业了。

CPU怎么用中断做到多任务运行呢?

CPU不是普通的人,CPU比普通的人快得多得多。

就拿我手上最差的新唐单片机举例子,他的主频是24M,那执行一条指令周期的时间就是

1/24000000 = 41.6 纳秒

假设你现在有两个事情要做,玩王者荣耀和扫地

你在第一个时钟周期,拿着手机玩王者荣耀,第二个时钟周期扫地,第三个时钟周期玩王者荣耀,第四个时钟周期扫地……

这样,你会发现,CPU是一个闪电侠,可能比闪电侠还要快,根据这个方法,可以裂变出很多很多个人做很多很多件事情。

Linux 内核中断上文为什么不能休眠呢?

现在的Linux 内核中断是不能嵌套的,所以我们就只讨论单中断的问题。

进中断的第一件事情,就是关中断,这个是关所有中断。

然后就在中断里面干事情了,具体做什么事情,我也不知道。

做完事情然后就开中断,去做其他事情了。

如果休眠会引起什么呢?

比如这样的代码

static irqreturn_t tpd_eint_interrupt_handler(unsigned irq, struct irq_desc *desc)
{
    TPD_DEBUG_PRINT_INT;
    tpd_flag = 1;

    /* enter EINT handler disable INT, make sure INT is disable when handle touch event including top/bottom half */
    /* use _nosync to avoid deadlock */
       spin_lock(&irq_lock);
    if (tpd_irq_flag) {
        tpd_irq_flag = 0;
        disable_irq_nosync(tpd_touch_irq);
    }
    spin_unlock(&irq_lock);
    ssleep(1);//系统会死掉
    wake_up_interruptible(&waiter);
    return IRQ_HANDLED;
}

这个代码的问题在ssleep(1)上,ssleep(1)是OS里面的休眠函数,这个函数会让任务休眠,休眠的意思就是让这个任务睡觉不执行,CPU去执行其他的任务。

因为CPU是按时间片执行的,等下一次调度到这个中断的时候,就有可能再获取一次spin_lock自旋锁,两次获取锁的操作,肯定就引起系统挂壁了。

具体的可以去看看ssleep的代码

/**
 * msleep - sleep safely even with waitqueue interruptions
 * @msecs: Time in milliseconds to sleep for
 */
void msleep(unsigned int msecs)
{
 unsigned long timeout = msecs_to_jiffies(msecs) + 1;

 while (timeout)
  timeout = schedule_timeout_uninterruptible(timeout);
}

EXPORT_SYMBOL(msleep);

系统宕机日志

[    1.564767] <3>.(3)[139:kworker/u8:2]<<GTP-INF>>[tpd_irq_registration:484] Device Tree Tpd_irq_registration!
[    1.566520] <3>.(3)[139:kworker/u8:2]gpiod_set_debounce: invalid GPIO
[    1.567319] <3>.(3)[139:kworker/u8:2]<<GTP-INF>>[tpd_irq_registration:495] Device gt1x_int_type = 1!
[    1.568484] <0>-(0)[0:swapper/0]BUG: scheduling while atomic: swapper/0/0/0x00010002
[    1.568493] <0>.(3)[139:kworker/u8:2]<<GTP-INF>>[tpd_irq_registration:514] irq:119, debounce:0-0:
[    1.570534] Modules linked in:
[    1.570912] <0>-(0)[0:swapper/0]CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.4.146 #41
[    1.571879] <0>-(0)[0:swapper/0]Hardware name: Generic DT based system
[    1.572689] Backtrace: 
[    1.572989] <0>-(0)[0:swapper/0][<c010d5d4>] (dump_backtrace) from [<c010d7fc>] (show_stack+0x18/0x1c)
[    1.574143]  r6:60000193 r5:c1136bc8 r4:00000000 r3:dc8ba692
[    1.574841] <0>-(0)[0:swapper/0][<c010d7e4>] (show_stack) from [<c041b4c4>] (dump_stack+0x94/0xa8)
[    1.575950] <0>-(0)[0:swapper/0][<c041b430>] (dump_stack) from [<c0155350>] (__schedule_bug+0x58/0x6c)
[    1.577104]  r6:c1101ce8 r5:c1085b40 r4:00000000 r3:60000193
[    1.577802] <0>-(0)[0:swapper/0][<c01552f8>] (__schedule_bug) from [<c0b6dc88>] (__schedule+0x59c/0x808)
[    1.578978]  r4:c110bcf8 r3:00010002
[    1.579418] <0>-(0)[0:swapper/0][<c0b6d6ec>] (__schedule) from [<c0b6df48>] (schedule+0x54/0xc4)
[    1.580507]  r10:00000000 r9:c1102100 r8:dfedfb80 r7:c1103948
[    1.581216] <0>-(0)[0:swapper/0][<c0b6def4>] (schedule) from [<c0b712c4>] (schedule_timeout+0x148/0x264)
[    1.582391]  r4:fffea370 r3:c1100000
[    1.582831] <0>-(0)[0:swapper/0][<c0b7117c>] (schedule_timeout) from [<c01ad8f8>] (msleep+0x34/0x40)
[    1.583964]  r10:c1100000 r9:00000000 r8:00000000 r7:00000077
[    1.584673] <0>-(0)[0:swapper/0][<c01ad8c4>] (msleep) from [<c081e0d0>] (tpd_eint_interrupt_handler+0x2c/0xd8)
[    1.585913]  r5:ced00000 r4:cec8d540
[    1.586353] <0>-(0)[0:swapper/0][<c081e0a4>] (tpd_eint_interrupt_handler) from [<c0199684>] (handle_irq_event_percpu+0x6c/0x268)
[    1.587787]  r5:d0344ae0 r4:cec8d540
[    1.588227] <0>-(0)[0:swapper/0][<c0199618>] (handle_irq_event_percpu) from [<c01998c0>] (handle_irq_event+0x40/0x64)
[    1.589542]  r10:c0c487cc r9:00000077 r8:00000000 r7:00000000
[    1.590251] <0>-(0)[0:swapper/0][<c0199880>] (handle_irq_event) from [<c019d388>] (handle_level_irq+0xb4/0x16c)
[    1.591502]  r6:d0344a90 r5:d0344ae0 r4:d0344a80 r3:00020002
[    1.592201] <0>-(0)[0:swapper/0][<c019d2d4>] (handle_level_irq) from [<c0198b64>] (generic_handle_irq+0x2c/0x3c)
[    1.593462]  r6:d030e6d0 r5:02000000 r4:00000019 r3:c019d2d4
[    1.594161] <0>-(0)[0:swapper/0][<c0198b38>] (generic_handle_irq) from [<c045d7e4>] (mtk_eint_irq_handler+0x288/0x36c)
[    1.595486] <0>-(0)[0:swapper/0][<c045d55c>] (mtk_eint_irq_handler) from [<c0198b64>] (generic_handle_irq+0x2c/0x3c)
[    1.596790]  r10:00000001 r9:00000000 r8:00010001 r7:00000000
[    1.597499] <0>-(0)[0:swapper/0][<c0198b38>] (generic_handle_irq) from [<c0198e7c>] (__handle_domain_irq+0xc0/0x25c)
[    1.598802] <0>-(0)[0:swapper/0][<c0198dbc>] (__handle_domain_irq) from [<c01015ec>] (gic_handle_irq+0x50/0x94)
[    1.600053]  r9:e1003000 r8:e1002000 r7:c1101eb0 r6:e100200c
[    1.600752] <0>-(0)[0:swapper/0][<c010159c>] (gic_handle_irq) from [<c010e314>] (__irq_svc+0x54/0x90)
[    1.601893] <0>-(0)[0:swapper/0]Exception stack(0xc1101eb0 to 0xc1101ef8)
[    1.602736] <0>-(0)[0:swapper/0]1ea0:                                     00000000 ffffffff 1ee35000 00000000
[    1.603965] <0>-(0)[0:swapper/0]1ec0: c11ac9b4 cfbdf800 5cc9dbdf 00000000 5ccecf4c 00000000 00000001 c1101f44
[    1.605192] <0>-(0)[0:swapper/0]1ee0: c1101f00 c1101f00 c08a2380 c08a2390 60000013 ffffffff
[    1.606224]  r9:00000000 r8:5ccecf4c r7:c1101ee4 r6:ffffffff
[    1.606922] <0>-(0)[0:swapper/0][<c08a2220>] (cpuidle_enter_state) from [<c08a2670>] (cpuidle_enter+0x1c/0x20)
[    1.608162]  r10:c1103a1c r9:c1187308 r8:cfbdf800 r7:c11ac9b4
[    1.608872] <0>-(0)[0:swapper/0][<c08a2654>] (cpuidle_enter) from [<c0183334>] (cpu_startup_entry+0x1e0/0x3d8)
[    1.610110] <0>-(0)[0:swapper/0][<c0183154>] (cpu_startup_entry) from [<c0b6ac18>] (rest_init+0x90/0x94)
[    1.611285]  r7:ffffffff
[    1.611596] <0>-(0)[0:swapper/0][<c0b6ab88>] (rest_init) from [<c1000e44>] (start_kernel+0x418/0x468)
[    1.612739]  r4:c11b1040 r3:dc8ba692
[    1.613179] <0>-(0)[0:swapper/0]Backtrace aborted due to bad pc <c1000a30>
[    1.614040] <0>-(0)[0:swapper/0]------------[ cut here ]------------
[    1.614816] <0>-(0)[0:swapper/0]Kernel BUG at c0155360 [verbose debug info unavailable]
[    1.615807] <0>-(0)[0:swapper/0]Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM
[    1.616746] <0>-(0)[0:swapper/0][LY]rtc_mark_aee_kernel_panic!!!
[    1.617551] disable aee kernel api
[    1.617935] <0>-(0)[0:swapper/0]Kernel Offset: disabled
[    1.618603] Modules linked in:

Linux 内核中断函数能不能执行malloc函数?

这个是之前已经发过的文章

可以在中断服务程序执行malloc吗?

内核里面分配内存的函数是kmalloc ,其他不废话,我直接测试了下面的代码

static irqreturn_t tpd_eint_interrupt_handler(unsigned irq, struct irq_desc *desc)
{
    char *rbuff = NULL;
    rbuff = kmalloc(1024, GFP_KERNEL);
    TPD_DEBUG_PRINT_INT;
    tpd_flag = 1;

    /* enter EINT handler disable INT, make sure INT is disable when handle touch event including top/bottom half */
    /* use _nosync to avoid deadlock */
       spin_lock(&irq_lock);
    if (tpd_irq_flag) {
        tpd_irq_flag = 0;
        disable_irq_nosync(tpd_touch_irq);
    }
    spin_unlock(&irq_lock);
    ssleep(1);//系统会死掉
    wake_up_interruptible(&waiter);
    kfree(rbuff);
    return IRQ_HANDLED;
}

系统是没有挂掉的,然后去看看kmalloc的实现。

/**
 * kmalloc - allocate memory
 * @size: how many bytes of memory are required.
 * @flags: the type of memory to allocate.
 *
 * kmalloc is the normal method of allocating memory
 * for objects smaller than page size in the kernel.
 *
 * The @flags argument may be one of:
 *
 * %GFP_USER - Allocate memory on behalf of user.  May sleep.
 *
 * %GFP_KERNEL - Allocate normal kernel ram.  May sleep.
 *
 * %GFP_ATOMIC - Allocation will not sleep.  May use emergency pools.
 *   For example, use this inside interrupt handlers.
 *
 * %GFP_HIGHUSER - Allocate pages from high memory.
 *
 * %GFP_NOIO - Do not do any I/O at all while trying to get memory.
 *
 * %GFP_NOFS - Do not make any fs calls while trying to get memory.
 *
 * %GFP_NOWAIT - Allocation will not sleep.
 *
 * %__GFP_THISNODE - Allocate node-local memory only.
 *
 * %GFP_DMA - Allocation suitable for DMA.
 *   Should only be used for kmalloc() caches. Otherwise, use a
 *   slab created with SLAB_DMA.
 *
 * Also it is possible to set different flags by OR'ing
 * in one or more of the following additional @flags:
 *
 * %__GFP_COLD - Request cache-cold pages instead of
 *   trying to return cache-warm pages.
 *
 * %__GFP_HIGH - This allocation has high priority and may use emergency pools.
 *
 * %__GFP_NOFAIL - Indicate that this allocation is in no way allowed to fail
 *   (think twice before using).
 *
 * %__GFP_NORETRY - If memory is not immediately available,
 *   then give up at once.
 *
 * %__GFP_NOWARN - If allocation fails, don't issue any warnings.
 *
 * %__GFP_REPEAT - If allocation fails initially, try once more before failing.
 *
 * There are other flags available as well, but these are not intended
 * for general use, and so are not documented here. For a full list of
 * potential flags, always refer to linux/gfp.h.
 */
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
 if (__builtin_constant_p(size)) {
  if (size > KMALLOC_MAX_CACHE_SIZE)
   return kmalloc_large(size, flags);
#ifndef CONFIG_SLOB
  if (!(flags & GFP_DMA)) {
   int index = kmalloc_index(size);

   if (!index)
    return ZERO_SIZE_PTR;

   return kmem_cache_alloc_trace(kmalloc_caches[index],
     flags, size);
  }
#endif
 }
 return __kmalloc(size, flags);
}

代码里面没有看到引起调度的代码,但是注释里面有说会引起休眠。

这个函数跟微信微信公众号留言提到的gfp一致。

推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值