1

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

例如:

// 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 
4

1 回答 1

1

我不太清楚“[您的]代码之外”是什么意思,所以如果这个答案没有帮助,请详细说明。您提供的文字代码将起作用,我猜您已经从实际失败的东西中减少了它?

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

分配生命周期

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

线程问题

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

更新:

关于您的子问题“我想到的是静态变量和使用 OSMalloc 分配的变量之间的内存位置差异。ctl_enqueuedata() 中的代码可以访问两者吗?”

是的。

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

于 2013-05-09T17:23:14.823 回答