浅析put_user之__put_user_1,2,4,8,bad
文章来源:http://gliethttp.cublog.cn
使用put_user对1、2、4、8字节数据的用户空间和内核空间互拷贝,执行速度要远高于copy_from_user和copy_to_user所以当互传输数据数据为1、2、4、8字节时[小于8的数据,3,5,6,7可以硬凑成1、2、4、8以牺牲空间换取效率gliethttp].
1.put_user宏 /include/asm-arm/Uaccess.h #define __put_user_x(__r1,__p,__e,__s,__i...) \ __asm__ __volatile__ ("bl __put_user_" #__s \ : "=&r" (__e) \//详见《浅析arm-linux内嵌汇编小程序》 : "0" (__p), "r" (__r1) \//p的寄存器作为输入直接赋值给r0寄存器 : __i) //告诉编译器:__i...等寄存器会在如下的应用中改变 #define put_user(x,p) \ ({ \ const register typeof(*(p)) __r1 asm("r1") = (x); \//告诉编译器:寄存器r1存放x数据值[2007-07-17 gliethttp] //如果数值为64位,那么编译器顺延r1,自动将r2作为高32位进行数据暂存 const register typeof(*(p)) *__p asm("r0") = (p); \//告诉编译器:寄存器r0存放p地址值 register int __e asm("r0"); \ switch (sizeof(*(p))) { \ case 1: \ __put_user_x(__r1, __p, __e, 1, "r2", "lr"); \ //r0=p;r1=__r1;__e=r0;[gliethttp] break; \ case 2: \ __put_user_x(__r1, __p, __e, 2, "r2", "lr"); \ break; \ case 4: \ __put_user_x(__r1, __p, __e, 4, "r2", "lr"); \ break; \ case 8: \ __put_user_x(__r1, __p, __e, 8, "ip", "lr"); \//64位,编译器自动将r2作为高32位进行数据暂存[gliethttp] break; \ default: __e = __put_user_bad(); break; \ } \ __e; \ }) 2.__put_user_1,__put_user_2,__put_user_4,__put_user_8,__put_user_bad arch/arm/lib/putuser.S __put_user_1,2,4,8,bad汇编源码: .global __put_user_1 __put_user_1: bic r2, sp, #0x1f00 bic r2, r2, #0x00ff ldr r2, [r2, #TSK_ADDR_LIMIT] //读取current->addr_limit空间上限值 sub r2, r2, #1 //以上4句分析在另一篇《浅析armlinux-sp进程栈结构》专议 cmp r0, r2 //比较r0中的地址值和进程地址上限值 1: strlsbt r1, [r0] //在自身进程地址范围内,则执行赋值操作[gliethttp],把存放到r1寄存器中的x赋值 movls r0, #0 //r0 = 0;表示操作成功 movls pc, lr //返回 b __put_user_bad //p值不在本进程地址范围内,跳到bad .global __put_user_2 __put_user_2: bic r2, sp, #0x1f00 bic r2, r2, #0x00ff ldr r2, [r2, #TSK_ADDR_LIMIT] //读取current->addr_limit空间上限值 sub r2, r2, #2 //以上4句分析在另一篇《浅析armlinux-SP进程栈结构》专议 cmp r0, r2 //比较r0中的地址值和进程地址上限值 2: strlsbt r1, [r0], #1 //16位赋值:低8字节赋值 movls r1, r1, lsr #8 //高8字节 3: strlsbt r1, [r0] //16位赋值:高8字节赋值 movls r0, #0 //r0 = 0;表示操作成功 movls pc, lr //返回 b __put_user_bad //p值不在本进程地址范围内,跳到bad .global __put_user_4 __put_user_4: bic r2, sp, #0x1f00 bic r2, r2, #0x00ff ldr r2, [r2, #TSK_ADDR_LIMIT] //读取current->addr_limit空间上限值 sub r2, r2, #4 //以上4句分析在另一篇《浅析armlinux-SP进程栈结构》专议 cmp r0, r2 //比较r0中的地址值和进程地址上限值 4: strlst r1, [r0] //4字节赋值 movls r0, #0 //r0 = 0;表示操作成功 movls pc, lr //返回 b __put_user_bad //p值不在本进程地址范围内,跳到bad .global __put_user_8 __put_user_8: bic ip, sp, #0x1f00 bic ip, ip, #0x00ff ldr ip, [ip, #TSK_ADDR_LIMIT] //读取current->addr_limit空间上限值 sub ip, ip, #8 //以上4句分析在另一篇《浅析armlinux-SP进程栈结构》专议 cmp r0, ip //比较r0中的地址值和进程地址上限值 5: strlst r1, [r0], #4 //低32位赋值 6: strlst r2, [r0] //r2存放高32位 movls r0, #0 //r0 = 0;表示操作成功 movls pc, lr //返回 /* fall through */ __put_user_bad: mov r0, #-14 //空间超出current->addr_limit范围 mov pc, lr 注:使用语句ulimit -s 可以查看进程栈的最大值,一般8k.详见《浅析armlinux-sp进程切换栈结构和切换函数__switch_to()》.
|