在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
答案 0 :(得分:1)
我不太清楚“你的代码之外”是什么意思,所以如果这个答案没有用,请详细说明。你提供的文字代码是可行的,我猜你已经从实际失败的东西中减少了它?
在这种情况下我可以想到两个可能的问题:
分配生命周期
静态变量的内存在加载kext时分配,在卸载时释放。你确定使用你的记忆是什么,在你的kext卸载之后肯定没有使用它吗?如果它是一个IOKit kext,内核将在加载后很快自动卸载它,除非其中一个人物匹配。这可能不是您和您的代码所期望的。
线程问题
基本上所有内核代码都是多线程的,你无法逃脱它。静态/全局变量特别容易受到竞争条件的影响。如果一个线程写入缓冲区而另一个线程试图通过printf()读取它,那么你就会遇到麻烦。您需要确保正确地序列化对缓冲区的访问,或使用不同的策略来管理缓冲区内存。如果缓冲区应该是临时的,那么在堆栈上分配它们(函数内的非static
)可能是一个更好的主意,具体取决于大小。正如@ Merlin069所提到的,内核堆栈非常小(<16kiB),因此避免大于几百字节的任何内容。除非它是一个递归函数,否则你的例子中的50字节缓冲区应该没问题。
<强>更新强>
关于你的子问题“我的想法是静态变量和OSMalloc分配的内存之间的差异.ctl_enqueuedata()中的代码可以同时访问吗?”
是
访问内核中分配的内存非常类似于在常规程序中执行此操作。 kernel_task有自己的内存映射,只要在内核模式下运行,它就会处于活动状态。内核是单片的,因此在一个kext中有效的指针在另一个kext中也是有效的。只有当你想从内核或用户空间的内核空间访问用户空间内存时,才必须明确地处理映射。