linux2.6 __copy_user

这个函数在不同的架构下有着不同的实现,因为它主要是用嵌入汇编写的,我将其简化后得到下面的代码。

#define __copy_user(to,from,size)
	int __d0, __d1, __d2;
	// 0为size、1为to、2为from
	__asm__ __volatile__(
		"	cmp  $7,%0\n" 
		"	jbe  1f\n"    // size < 7跳转到标签1
		"	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");

函数的参数分别是:
to:写入地址
from:源地址
size:写入量

嵌入汇编的输出"=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2)
=&c"(size):size使用ecx寄存器,=表示只能用于写,&表示输出限定,表示ecx只作为输出用,不会被输入使用。
"=&D" (__d0):表示__do使用di寄存器,只用于写。
"=&S" (__d1): 表示__d1使用si寄存器,只用于写。
"=r"(__d2) :表示__d2使用寄存器操作数(r表示general寄存器操作数)

汇编输入 "3"(size), "0"(size), "1"(to), "2"(from)
数字表示他们对应的操作数,3表示与输出的"=r"(__d2)一样使用内存来写入,0使用的ecx、1使用di、2使用si

汇编代码首先:

cmp  $7,%0\n
jbe  1f

如果size < 7跳转到标签1,将ds:si(from)的ecx(size)个字节复制到es:di(to)

1:	rep; movsb

如果size >= 7那么继续向下执行。

	movl %1,%0 // 将to保存到ecx
	negl %0  		 // ecx取负
	andl $7,%0   //  ecx与7,与上一步一起相当于取余%8
	subl %0,%3  //  %3是内存中的变量,保存的是(size - size%8)的值
4:	rep; movsb   // 传输ds:si到es:di,将余数按字节复制到目的内存

刚刚把余数复制过去了,现在需要把剩下的大块对齐的内存复制

	movl %3,%0
	shrl $2,%0
	andl $3,%3
	.align 2,0x90
0:	rep; movsl   // 传送双字,也就是每次传输4字节

参考

  1. gcc嵌入汇编 https://dirtysalt.github.io/html/gcc-asm.html

你可能感兴趣的:(Linux内核)