linux get_user,linux内核中的get_user和put_user
內核版本:2.6.14
CPU平臺:arm本文引用地址:http://www.eepw.com.cn/article/201611/319994.htm
在內核空間和用戶空間交換數據時,get_user和put_user是兩個兩用的函數。相對于copy_to_user和copy_from_user(將在另一篇博客中分析),這兩個函數主要用于完成一些簡單類型變量(char、int、long等)的拷貝任務,對于一些復合類型的變量,比如數據結構或者數組類型,get_user和put_user函數還是無法勝任,這兩個函數內部將對指針指向的對象長度進行檢查,在arm平臺上只支持長度為1,2,4,8的變量。下面我具體分析,首先看get_user的定義(linux/include/asm-arm/uaccess.h):
[plain]view plaincopy
print?externint__get_user_1(void*);
externint__get_user_2(void*);
externint__get_user_4(void*);
externint__get_user_8(void*);
externint__get_user_bad(void);
#define__get_user_x(__r2,__p,__e,__s,__i...)\
__asm____volatile__(\
__asmeq("%0","r0")__asmeq("%1","r2")\//進行判斷(#define__asmeq(x,y)".ifnc"x","y";.err;.endif\n\t")
"bl__get_user_"#__s\//根據參數調用不同的函數,此時r0=指向用戶空間的指針,r2=內核空間的變量
:"=&r"(__e),"=r"(__r2)\
:"0"(__p)\
:__i,"cc")
#defineget_user(x,p)\
({\
constregistertypeof(*(p))__user*__pasm("r0")=(p);\//__p的數據類型和*(p)的指針數據類型是一樣的,__p=p,且存放在r0寄存器中
registertypeof(*(p))__r2asm("r2");\//__r2的數據類型和*(p)的數據類型是一樣的,且存放在r2寄存器中
registerint__easm("r0");\//定義__e,存放在寄存器r0,作為返回值
switch(sizeof(*(__p))){\//對__p所指向的對象長度進行檢查,并根據長度調用響應的函數
case1:\
__get_user_x(__r2,__p,__e,1,"lr");\
break;\
case2:\
__get_user_x(__r2,__p,__e,2,"r3","lr");\
break;\
case4:\
__get_user_x(__r2,__p,__e,4,"lr");\
break;\
case8:\
__get_user_x(__r2,__p,__e,8,"lr");\
break;\
default:__e=__get_user_bad();break;\//默認處理
}\
x=__r2;\
__e;\
})
上面的源碼涉及到gcc的內聯匯編,不太了解的朋友可以參考前面的博客(http://blog.csdn.net/ce123/article/details/8209702)。繼續,跟蹤__get_user_1等函數的執行,它們的定義如下(linux/arch/arm/lib/getuser.S)。
[plain]view plaincopy
print?.global__get_user_1
__get_user_1:
1:ldrbtr2,[r0]
movr0,#0
movpc,lr
.global__get_user_2
__get_user_2:
2:ldrbtr2,[r0],#1
3:ldrbtr3,[r0]
#ifndef__ARMEB__
orrr2,r2,r3,lsl#8
#else
orrr2,r3,r2,lsl#8
#endif
movr0,#0
movpc,lr
.global__get_user_4
__get_user_4:
4:ldrtr2,[r0]
movr0,#0
movpc,lr
.global__get_user_8
__get_user_8:
5:ldrtr2,[r0],#4
6:ldrtr3,[r0]
movr0,#0
movpc,lr
__get_user_bad_8:
movr3,#0
__get_user_bad:
movr2,#0
movr0,#-EFAULT
movpc,lr
.section__ex_table,"a"
.long1b,__get_user_bad
.long2b,__get_user_bad
.long3b,__get_user_bad
.long4b,__get_user_bad
.long5b,__get_user_bad_8
.long6b,__get_user_bad_8
.previous
這段代碼都是單條匯編指令實現的內存操作,就不進行詳細注解了。如果定義__ARMEB__宏,則是支持EABI的大端格式代碼(http://blog.csdn.net/ce123/article/details/8457491),關于大端模式和小端模式的詳細介紹,可以參考http://blog.csdn.net/ce123/article/details/6971544。這段代碼在.section __ex_table, "a"之前都是常規的內存拷貝操縱,特殊的地方在于后面定義“__ex_table”section 。
標號1,2,...,6處是內存訪問指令,如果mov的源地址位于一個尚未被提交物理頁面的空間中,將產生缺頁異常,內核會調用do_page_fault函數處理這個異常,因為異常發生在內核空間,do_page_fault將調用search_exception_tables在“__ex_table”中查找異常指令的修復指令,在上面這段帶面的最后,“__ex_table”section 中定義了如下數據:
[plain]view plaincopy
print?.section__ex_table,"a"
.long1b,__get_user_bad//其中1b對應標號1處的指令,__get_user_bad是1處指令的修復指令。
.long2b,__get_user_bad
.long3b,__get_user_bad
.long4b,__get_user_bad
.long5b,__get_user_bad_8
.long6b,__get_user_bad_8當標號1處發生缺頁異常時,系統將調用do_page_fault提交物理頁面,然后跳到__get_user_bad繼續執行。get_user函數如果成果執行則返回1,否則返回-EFAULT。
put_user用于將內核空間的一個簡單類型變量x拷貝到p所指向的用戶空間。該函數可以自動判斷變量的類型,如果執行成功則返回0,否則返回-EFAULT。下面給出它們的定義(linux/include/asm-arm/uaccess.h)。
[plain]view plaincopy
print?externint__put_user_1(void*,unsignedint);
externint__put_user_2(void*,unsignedint);
externint__put_user_4(void*,unsignedint);
externint__put_user_8(void*,unsignedlonglong);
externint__put_user_bad(void);
#define__put_user_x(__r2,__p,__e,__s)\
__asm____volatile__(\
__asmeq("%0","r0")__asmeq("%2","r2")\
"bl__put_user_"#__s\
:"=&r"(__e)\
:"0"(__p),"r"(__r2)\
:"ip","lr","cc")
#defineput_user(x,p)\
({\
constregistertypeof(*(p))__r2asm("r2")=(x);\
constregistertypeof(*(p))__user*__pasm("r0")=(p);\
registerint__easm("r0");\
switch(sizeof(*(__p))){\
case1:\
__put_user_x(__r2,__p,__e,1);\
break;\
case2:\
__put_user_x(__r2,__p,__e,2);\
break;\
case4:\
__put_user_x(__r2,__p,__e,4);\
break;\
case8:\
__put_user_x(__r2,__p,__e,8);\
break;\
default:__e=__put_user_bad();break;\
}\
__e;\
})__put_user_1等函數的的定義如下(linux/arch/arm/lib/putuser.S)。
[plain]view plaincopy
print?.global__put_user_1
__put_user_1:
1:strbtr2,[r0]
movr0,#0
movpc,lr
.global__put_user_2
__put_user_2:
movip,r2,lsr#8
#ifndef__ARMEB__
2:strbtr2,[r0],#1
3:strbtip,[r0]
#else
2:strbtip,[r0],#1
3:strbtr2,[r0]
#endif
movr0,#0
movpc,lr
.global__put_user_4
__put_user_4:
4:strtr2,[r0]
movr0,#0
movpc,lr
.global__put_user_8
__put_user_8:
5:strtr2,[r0],#4
6:strtr3,[r0]
movr0,#0
movpc,lr
__put_user_bad:
movr0,#-EFAULT
movpc,lr
.section__ex_table,"a"
.long1b,__put_user_bad
.long2b,__put_user_bad
.long3b,__put_user_bad
.long4b,__put_user_bad
.long5b,__put_user_bad
.long6b,__put_user_bad
.previousput_user函數就不具體分析了。get_user和put_user僅能完成一些簡單類型變量的拷貝任務,后面我們將分析copy_to_user和copy_from_user。
總結
以上是生活随笔為你收集整理的linux get_user,linux内核中的get_user和put_user的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DWG TrueView 2013 –
- 下一篇: 明日方舟骑兵与猎人活动良心来袭 叉叉助手