静态变量会导致OSX网络崩溃



在mac OS X网络内核扩展中,我注意到,如果我有一个静态分配的缓冲区,而不是一个动态的,这会导致内核恐慌时调用API函数,如printf()或send(), ctl_enqueuedata(),以及许多其他。这就好像静态分配的缓冲区不能从我的代码外部读取或写入。

例如

:

// This is OK
static char* somevar = NULL;
somevar = OSMalloc(50, myOSMallocTag);
bzero(somevar, 50);
// This will create a kernel panic when used outside my code
static char somevar[50];
bzero(somevar, 50);

为什么?

编辑:我正要发布的代码,但它是冗长的,唯一的区别之间的版本工作和一个引起恐慌是如上所述。我想到的是静态变量和OSMalloc分配的变量在内存位置上的区别。ctl_enqueuedata()中的代码可以同时访问吗?

结果如下:

panic(cpu 0 caller 0xffffff802eeb7e95): Kernel trap at 0xffffff802ee28896, type 14=page fault, registers:
CR0: 0x0000000080010033, CR2: 0x0000000000000031, CR3: 0x000000024fbac0a7, CR4: 0x00000000001606e0
RAX: 0x000000007fffff01, RBX: 0x0000000000000000, RCX: 0x0000000000000010, RDX: 0xffffff7fb0d4d573
RSP: 0xffffff811f6fbae0, RBP: 0xffffff811f6fbbe0, RSI: 0x000000007fffffff, RDI: 0x0000000000000073
R8:  0x0000000000000000, R9:  0x0000000000000031, R10: 0x0000000000000000, R11: 0x0000000000000000
R12: 0x0000000000000000, R13: 0x0000000000000019, R14: 0xffffff811f6fbd01, R15: 0x0000000000000031
RFL: 0x0000000000010246, RIP: 0xffffff802ee28896, CS:  0x0000000000000008, SS:  0x0000000000000010
Fault CR2: 0x0000000000000031, Error code: 0x0000000000000000, Fault CPU: 0x0
Backtrace (CPU 0), Frame : Return Address
0xffffff811f6fb780 : 0xffffff802ee1d626 
0xffffff811f6fb7f0 : 0xffffff802eeb7e95 
0xffffff811f6fb9c0 : 0xffffff802eecd4dd 
0xffffff811f6fb9e0 : 0xffffff802ee28896 
0xffffff811f6fbbe0 : 0xffffff802f174a62 
0xffffff811f6fbc00 : 0xffffff7fb0d4cead 
0xffffff811f6fbd40 : 0xffffff7fb0d46101 
0xffffff811f6fbdf0 : 0xffffff802f150525 
0xffffff811f6fbe40 : 0xffffff802f1990b2 
0xffffff811f6fbef0 : 0xffffff802f1a04f2 
0xffffff811f6fbf50 : 0xffffff802f1e063a 
0xffffff811f6fbfb0 : 0xffffff802eecdd23 

我不太清楚你所说的"在[你的]代码之外"是什么意思,所以如果这个答案没有帮助,请详细说明。您提供的字面代码将会工作,我猜您已经从实际上失败的东西中简化了它?

在这种情况下,我可以想到两个可能的问题:

分配一生

静态变量的内存在加载ext时分配,在卸载ext时释放。你确定正在使用你的内存的东西在你的下一个卸载之后绝对没有使用它吗?如果它是IOKit ext,内核将在加载后很快自动卸载它,除非其中一个个性匹配。这可能不是你和你的代码所期望的。

线程问题

本质上,所有内核代码都是多线程的,你不能转义它。静态/全局变量特别容易受到竞争条件的影响。如果一个线程正在写缓冲区,而另一个线程试图通过printf()读取缓冲区,那么您就是在自找麻烦。您需要确保正确地序列化对缓冲区的访问,或者使用不同的策略来管理缓冲区内存。如果缓冲区应该是临时的,那么根据缓冲区的大小,在堆栈上分配它们(函数内的非static) 可能是一个更好的主意。正如@Merlin069所提到的,内核堆栈非常小(16kiB),所以要避免任何大于几百字节的东西。除非它是一个递归函数,否则你的例子中的50字节缓冲区应该没问题。

更新:

关于你的子问题"我想到的是静态变量和OSMalloc分配的内存位置之间的差异。"ctl_enqueuedata()中的代码可以同时访问吗?"

是的。

访问内核中分配的内存很像在常规程序中这样做。kernel_task有自己的内存映射,它在内核模式下运行时处于活动状态。内核是整体的,因此在一个ext中有效的指针在另一个ext中也有效。只有当您想要从内核访问用户空间内存,或者从用户空间访问内核空间时,您才需要显式地处理映射。

最新更新