我最近在自定义 Linux 内核(2.6.31.5,x86)驱动程序中遇到一个问题,其中 copy_to_user 会定期不将任何字节复制到用户空间。它将返回传递给它的字节数,表明它没有复制任何内容。经过代码检查,我们发现代码在调用 copy_to_user 时禁用中断,这违反了它的合同。更正此问题后,该问题不再发生。由于该问题很少发生,因此我需要证明禁用中断导致了该问题。
如果您查看 arch/x86/lib/usercopy_32.c 代表中的以下代码片段; 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)
我的两个想法是:
- 当中断被禁用时,不会发生页面错误并且
然后代表;移动?被跳过而不做任何事情。返回值
那么将是 CX,或者未复制到用户空间的数量,因为
定义指定并观察到的行为。
- 页面错误确实发生了,但是linux无法处理它,因为中断被禁用,因此页面错误处理程序会跳过该指令,尽管我不知道页面错误处理程序将如何执行此操作。同样,在这种情况下,CX 将保持不变,并且返回值将是正确的。
谁能向我指出英特尔手册中指定此行为的部分,或者向我指出任何可能有帮助的其他 Linux 源代码?