日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux get_user,linux内核中的get_user和put_user

發布時間:2023/12/29 linux 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。