新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > linux內核中的get_user和put_user

        linux內核中的get_user和put_user

        作者: 時間:2016-11-22 來源:網絡 收藏
        內核版本:2.6.14

        CPU平臺:arm

        本文引用地址:http://www.104case.com/article/201611/319994.htm

        在內核空間和用戶空間交換數據時,get_userput_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?
        1. externint__get_user_1(void*);
        2. externint__get_user_2(void*);
        3. externint__get_user_4(void*);
        4. externint__get_user_8(void*);
        5. externint__get_user_bad(void);
        6. #define__get_user_x(__r2,__p,__e,__s,__i...)
        7. __asm____volatile__(
        8. __asmeq("%0","r0")__asmeq("%1","r2")//進行判斷(#define__asmeq(x,y)".ifnc"x","y";.err;.endifnt")
        9. "bl__get_user_"#__s//根據參數調用不同的函數,此時r0=指向用戶空間的指針,r2=內核空間的變量
        10. :"=&r"(__e),"=r"(__r2)
        11. :"0"(__p)
        12. :__i,"cc")
        13. #defineget_user(x,p)
        14. ({
        15. constregistertypeof(*(p))__user*__pasm("r0")=(p);//__p的數據類型和*(p)的指針數據類型是一樣的,__p=p,且存放在r0寄存器中
        16. registertypeof(*(p))__r2asm("r2");//__r2的數據類型和*(p)的數據類型是一樣的,且存放在r2寄存器中
        17. registerint__easm("r0");//定義__e,存放在寄存器r0,作為返回值
        18. switch(sizeof(*(__p))){//對__p所指向的對象長度進行檢查,并根據長度調用響應的函數
        19. case1:
        20. __get_user_x(__r2,__p,__e,1,"lr");
        21. break;
        22. case2:
        23. __get_user_x(__r2,__p,__e,2,"r3","lr");
        24. break;
        25. case4:
        26. __get_user_x(__r2,__p,__e,4,"lr");
        27. break;
        28. case8:
        29. __get_user_x(__r2,__p,__e,8,"lr");
        30. break;
        31. default:__e=__get_user_bad();break;//默認處理
        32. }
        33. x=__r2;
        34. __e;
        35. })

        上面的源碼涉及到gcc的內聯匯編,不太了解的朋友可以參考前面的博客(http://blog.csdn.net/ce123/article/details/8209702)。繼續,跟蹤__get_user_1等函數的執行,它們的定義如下(linux/arch/arm/lib/getuser.S)。

        [plain]view plaincopy
        print?
        1. .global__get_user_1
        2. __get_user_1:
        3. 1:ldrbtr2,[r0]
        4. movr0,#0
        5. movpc,lr
        6. .global__get_user_2
        7. __get_user_2:
        8. 2:ldrbtr2,[r0],#1
        9. 3:ldrbtr3,[r0]
        10. #ifndef__ARMEB__
        11. orrr2,r2,r3,lsl#8
        12. #else
        13. orrr2,r3,r2,lsl#8
        14. #endif
        15. movr0,#0
        16. movpc,lr
        17. .global__get_user_4
        18. __get_user_4:
        19. 4:ldrtr2,[r0]
        20. movr0,#0
        21. movpc,lr
        22. .global__get_user_8
        23. __get_user_8:
        24. 5:ldrtr2,[r0],#4
        25. 6:ldrtr3,[r0]
        26. movr0,#0
        27. movpc,lr
        28. __get_user_bad_8:
        29. movr3,#0
        30. __get_user_bad:
        31. movr2,#0
        32. movr0,#-EFAULT
        33. movpc,lr
        34. .section__ex_table,"a"
        35. .long1b,__get_user_bad
        36. .long2b,__get_user_bad
        37. .long3b,__get_user_bad
        38. .long4b,__get_user_bad
        39. .long5b,__get_user_bad_8
        40. .long6b,__get_user_bad_8
        41. .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?
        1. .section__ex_table,"a"
        2. .long1b,__get_user_bad//其中1b對應標號1處的指令,__get_user_bad是1處指令的修復指令。
        3. .long2b,__get_user_bad
        4. .long3b,__get_user_bad
        5. .long4b,__get_user_bad
        6. .long5b,__get_user_bad_8
        7. .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?
        1. externint__put_user_1(void*,unsignedint);
        2. externint__put_user_2(void*,unsignedint);
        3. externint__put_user_4(void*,unsignedint);
        4. externint__put_user_8(void*,unsignedlonglong);
        5. externint__put_user_bad(void);
        6. #define__put_user_x(__r2,__p,__e,__s)
        7. __asm____volatile__(
        8. __asmeq("%0","r0")__asmeq("%2","r2")
        9. "bl__put_user_"#__s
        10. :"=&r"(__e)
        11. :"0"(__p),"r"(__r2)
        12. :"ip","lr","cc")
        13. #defineput_user(x,p)
        14. ({
        15. constregistertypeof(*(p))__r2asm("r2")=(x);
        16. constregistertypeof(*(p))__user*__pasm("r0")=(p);
        17. registerint__easm("r0");
        18. switch(sizeof(*(__p))){
        19. case1:
        20. __put_user_x(__r2,__p,__e,1);
        21. break;
        22. case2:
        23. __put_user_x(__r2,__p,__e,2);
        24. break;
        25. case4:
        26. __put_user_x(__r2,__p,__e,4);
        27. break;
        28. case8:
        29. __put_user_x(__r2,__p,__e,8);
        30. break;
        31. default:__e=__put_user_bad();break;
        32. }
        33. __e;
        34. })
        __put_user_1等函數的的定義如下(linux/arch/arm/lib/putuser.S)。

        [plain]view plaincopy
        print?
        1. .global__put_user_1
        2. __put_user_1:
        3. 1:strbtr2,[r0]
        4. movr0,#0
        5. movpc,lr
        6. .global__put_user_2
        7. __put_user_2:
        8. movip,r2,lsr#8
        9. #ifndef__ARMEB__
        10. 2:strbtr2,[r0],#1
        11. 3:strbtip,[r0]
        12. #else
        13. 2:strbtip,[r0],#1
        14. 3:strbtr2,[r0]
        15. #endif
        16. movr0,#0
        17. movpc,lr
        18. .global__put_user_4
        19. __put_user_4:
        20. 4:strtr2,[r0]
        21. movr0,#0
        22. movpc,lr
        23. .global__put_user_8
        24. __put_user_8:
        25. 5:strtr2,[r0],#4
        26. 6:strtr3,[r0]
        27. movr0,#0
        28. movpc,lr
        29. __put_user_bad:
        30. movr0,#-EFAULT
        31. movpc,lr
        32. .section__ex_table,"a"
        33. .long1b,__put_user_bad
        34. .long2b,__put_user_bad
        35. .long3b,__put_user_bad
        36. .long4b,__put_user_bad
        37. .long5b,__put_user_bad
        38. .long6b,__put_user_bad
        39. .previous
        put_user函數就不具體分析了。get_user和put_user僅能完成一些簡單類型變量的拷貝任務,后面我們將分析copy_to_user和copy_from_user。


        評論


        技術專區

        關閉
        主站蜘蛛池模板: 陈巴尔虎旗| 祁东县| 钟山县| 加查县| 南宁市| 古浪县| 珲春市| 定州市| 龙口市| 安化县| 酒泉市| 临泉县| 邵阳市| 汝城县| 武宣县| 舞钢市| 平阳县| 朝阳市| 黑河市| 凤阳县| 平遥县| 平武县| 永修县| 墨竹工卡县| 深泽县| 铜川市| 乌兰浩特市| 赤城县| 惠东县| 阜康市| 建始县| 鸡泽县| 太康县| 革吉县| 武清区| 陵川县| 华安县| 邵阳市| 蕉岭县| 汉川市| 开平市|