我最近在自定义 Linux 内核(2.6.31.5,x86)驱动程序中遇到了一个问题,其中 copy_to_user 不会定期将任何字节复制到用户空间。它会返回传递给它的字节数,表明它没有复制任何东西。经过代码检查,我们发现代码在调用 copy_to_user 时禁用了中断,这违反了它的合同。更正此问题后,问题停止发生。因为这个问题很少发生,我需要证明禁用中断导致了这个问题。
如果您从 arch/x86/lib/usercopy_32.c rep 查看下面的代码片段;movsl 通过 CX 中的计数将单词复制到用户空间。大小在退出时用 CX 更新。如果 movsl 正确执行,CX 将为 0。因为 CX 不为零,所以 movs? 为了符合 copy_to_user 的定义和观察到的行为,必须没有执行指令。
/* Generic arbitrary sized copy. */
#define __copy_user(to, from, size) \
do { \
int __d0, __d1, __d2; \
__asm__ __volatile__( \
" cmp $7,%0\n" \
" jbe 1f\n" \
" movl %1,%0\n" \
" negl %0\n" \
" andl $7,%0\n" \
" subl %0,%3\n" \
"4: rep; movsb\n" \
" movl %3,%0\n" \
" shrl $2,%0\n" \
" andl $3,%3\n" \
" .align 2,0x90\n" \
"0: rep; movsl\n" \
" movl %3,%0\n" \
"1: rep; movsb\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"5: addl %3,%0\n" \
" jmp 2b\n" \
"3: lea 0(%3,%0,4),%0\n" \
" jmp 2b\n" \
".previous\n" \
".section __ex_table,\"a\"\n" \
" .align 4\n" \
" .long 4b,5b\n" \
" .long 0b,3b\n" \
" .long 1b,2b\n" \
".previous" \
: "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2) \
: "3"(size), "0"(size), "1"(to), "2"(from) \
: "memory"); \
} while (0)
我的两个想法是:
- 当中断被禁用时,页面错误不会发生然后rep;动?被跳过而不做任何事情。然后返回值将是 CX,或未复制到用户空间的数量,如定义指定和观察到的行为。
- 确实发生了页面错误,但是由于禁用了中断,linux无法处理它,因此页面错误处理程序跳过了该指令,尽管我不知道页面错误处理程序会如何执行此操作。同样,在这种情况下,CX 将保持不变并且返回值将是正确的。
谁能指出英特尔手册中指定此行为的部分,或者指出任何其他可能有用的 Linux 源代码?