第687章 看不见的沙漏!
`KERNEL PANIC - OUT OF MEMORY`
血红色的屏幕,如同地狱的判词,瞬间将整个实验室,从完美的乌托邦,拉回了残酷的现实。
“内存耗尽?”
“怎么可能!怎么会内存耗尽!”
黄建功猛地从椅子上弹了起来,不敢相信自己的眼睛。
内核恐慌!
这个他们以为,在解决了“堆栈隔离”问题后,就再也不会见到的噩梦,竟然以一种全新的,更加诡异的方式,再次降临!
而且,错误的原因,是“内存耗尽”!
这怎么可能?
他们的系统,现在总共就运行了五个极其简单的任务。
内核本身,加上五个任务的堆栈,所占用的内存,加起来也不到100KB。
而“盘古之心”拥有整整4MB的物理内存!
这就好像,一个拥有着巨大湖泊的人,却被告知,他因为缺水而渴死了!
这完全不符合逻辑!
“快!查!”黄建功对着整个实验室,发出了嘶吼,“查明原因!是不是硬件出了问题?内存条坏了吗?”
周老立刻带着人,冲向了硬件检测台,对“盘古之心”的内存模块,进行紧急检测。
而钱学敏和孙立国,则第一时间,冲到了主控台前,开始疯狂地,翻阅系统崩溃前,留下的最后几页日志。
试图从那片数据的汪洋中,找到一丝线索。
几分钟后。
周老脸色凝重地走了回来。
“内存,没有问题。”
“我们做了最高强度的读写测试,4MB的每一个字节,都完好无损。”
这个消息,让所有人的心,沉得更深了。
硬件没问题。
那就意味着,问题,百分之百,又出在了他们引以为傲的,“天枢”内核上!
“建功,你看这里!”
钱学敏突然指着屏幕上的一片数据,她的声音,带着一丝不易察觉的,颤抖。
黄建功立刻凑了过去。
那是一段由内核内存管理器,在每次分配内存时,打印出的调试日志。
`[ 1859.235] kmalloc: allocated 64 bytes at 0x0012c800. free_mem: 3.82 MB`
`[ 1859.578] kmalloc: allocated 64 bytes at 0x0012c840. free_mem: 3.82 MB`
`...`
这些日志,记录了每一次内核为内部数据结构(比如任务控制块TCB)分配内存的动作,以及分配后,系统剩余的空闲内存大小。
刚开始,一切正常。
系统剩余内存,一直稳定在3.8MB左右。
但当黄建功将日志,快速地向后翻动,翻到接近系统崩溃的那个时间点时。
他的瞳孔,猛地一缩。
`[ 1860.112] kmalloc: allocated 64 bytes at 0x003f8000. free_mem: 0.12 MB`
`[ 1860.455] kmalloc: allocated 64 bytes at 0x003f8040. free_mem: 0.12 MB`
`...`
`[ 1861.988] kmalloc: allocated 64 bytes at 0x003fffc0. free_mem: 64 bytes`
`[ 1862.331] kmalloc: FAILED! Cannot allocate 64 bytes!`
日志,清晰地,记录下了整个“死亡”的过程!
系统的空闲内存,在以一种肉眼可见的速度,被不断地蚕食!
从3.8MB,到1MB,到100KB,再到最后的几十个字节……
直到最后,当内核再次试图申请64个字节的内存时,它绝望地发现,整个内存池,已经空了。
于是,它触发了最严重的“内核恐慌”,整个系统,轰然倒塌。
“这……这到底是为什么?”一个年轻的研究员,声音发颤地问道,“我们的系统,从启动之后,就再也没有创建过新的任务。为什么内核会一直,不停地,在申请新的内存?”
是啊。
为什么?
这就像一个看不见的沙漏。
在所有人都没有察觉到的情况下,一点一点地,漏光了整个系统的生命之源。
黄建功死死地盯着那几行日志,大脑在疯狂地运转。
`kmalloc`... `kmalloc`... `kmalloc`...
内核在不停地分配内存。
但是,它分配了,却没有“释放”!
一个可怕的词汇,如同惊雷般,在他的脑海中炸响。
“内存泄漏(Memory Leak)!”
当他将这个词说出口时,钱学敏的脸色,瞬间变得惨白。
她也想到了。
在计算机科学中,这是一种最常见,也最阴险的错误。
程序在运行过程中,不断地向系统申请内存,但当这些内存不再被使用时,却没有及时地,将它们归还给系统。
日积月累。
这些被“遗忘”的,无法被再次使用的内存,就像血管里的血栓,会一点一点地,堵死整个系统。
“我们在哪里泄漏了内存?”黄建功喃喃自语。
他开始疯狂地,在脑中,回顾“天枢”V0.3的每一行业务逻辑。
创建任务时,分配TCB,分配堆栈。
这都是一次性的。
之后,系统就在五个任务之间,不断地切换,调度。
切换……调度……
每一次切换,内核都需要保存当前任务的上下文……
每一次调度,内核都需要测量切换的成本,计算新的时间片……
这个过程,会产生新的内存分配吗?
黄建功的目光,猛地落在了那段关于“上下文切换成本”的日志上。
`Switch cost: 12 us.`
`Switch cost: 15 us.`
`Switch cost: 73 us.`
为了计算这个成本,内核需要在切换前后,两次读取HPET的计数值。
然后,进行一次减法运算。
为了存储这两个计数值,和那个最终的差值,内核需要……临时的变量!
而这些临时变量,是存放在……
“堆栈里!”钱学敏和黄建功,几乎在同一时刻,失声喊了出来!
他们瞬间,就定位到了那个“看不见的沙漏”,到底藏在哪里!
每一次!
每一次时间中断发生时,CPU都会将当前任务的执行现场,压入内核的堆栈。
然后,跳转到中断服务程序。
中断服务程序,也就是他们的调度器,为了计算切换成本,又在内核堆栈上,定义了几个临时的变量。
当调度完成,切换到下一个用户任务时。
那些被中断压入的用户任务现场,会被正确地弹出。
但是!
他们忘了!
他们忘了去“清理”,那些在中断服务程序中,使用过的,临时的,属于内核自己的,堆栈空间!
每一次中断,内核的堆栈指针,都会因为这些被遗忘的临时变量,而向下,移动那么微不足道的,几个字节。
一次中断,几个字节。
一秒钟,一百次中断,就是几百个字节。
三十分钟……
黄建功的心,凉了半截。
他终于明白,那4MB的内存,是怎么被一点一点,耗光的了。
不是用户任务的错。
也不是内存管理器的错。
是他们自己!
是他们亲手编写的,那个被他们视为“绝对公平”的调度器,在每一次“心跳”的时候,都在为自己,挖深一点点的,坟墓!
(https://www.shubada.com/105253/39447634.html)
1秒记住书吧达:www.shubada.com。手机版阅读网址:m.shubada.com