在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > linux內(nèi)核中的copy_to_user和copy_from_user(一)

            linux內(nèi)核中的copy_to_user和copy_from_user(一)

            作者: 時間:2016-11-22 來源:網(wǎng)絡(luò) 收藏
            Kernel version:2.6.14

            CPU architecture:ARM920T

            本文引用地址:http://www.biyoush.com/article/201611/319995.htm

            Author:ce123(http://blog.csdn.net/ce123)

            1.copy_from_user

            在學(xué)習(xí)Linux內(nèi)核驅(qū)動的時候,經(jīng)常會碰到copy_from_user和copy_to_user這兩個函數(shù),設(shè)備驅(qū)動程序中的ioctl函數(shù)就經(jīng)常會用到。這兩個函數(shù)負(fù)責(zé)在用戶空間和內(nèi)核空間傳遞數(shù)據(jù)。首先看看它們的定義(linux/include/asm-arm/uaccess.h),先看copy_from_user:

            [plain]view plaincopy
            print?
            1. staticinlineunsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongn)
            2. {
            3. if(access_ok(VERIFY_READ,from,n))
            4. n=__arch_copy_from_user(to,from,n);
            5. else/*securityhole-plugit*/
            6. memzero(to,n);
            7. returnn;
            8. }

            先看函數(shù)的三個參數(shù):*to是內(nèi)核空間的指針,*from是用戶空間指針,n表示從用戶空間想內(nèi)核空間拷貝數(shù)據(jù)的字節(jié)數(shù)。如果成功執(zhí)行拷貝操作,則返回0,否則返回還沒有完成拷貝的字節(jié)數(shù)。

            這個函數(shù)從結(jié)構(gòu)上來分析,其實都可以分為兩個部分:
            1. 首先檢查用戶空間的地址指針是否有效;
            2. 調(diào)用__arch_copy_from_user函數(shù)。

            1.1.access_ok

            access_ok用來對用戶空間的地址指針from作某種有效性檢驗,這個宏和體系結(jié)構(gòu)相關(guān),在arm平臺上為(linux/include/asm-arm/uaccess.h):

            [plain]view plaincopy
            print?
            1. #define__range_ok(addr,size)({
            2. unsignedlongflag,sum;
            3. __chk_user_ptr(addr);
            4. __asm__("adds%1,%2,%3;sbcccs%1,%1,%0;movcc%0,#0"
            5. :"=&r"(flag),"=&r"(sum)
            6. :"r"(addr),"Ir"(size),"0"(current_thread_info()->addr_limit)
            7. :"cc");
            8. flag;})
            9. #defineaccess_ok(type,addr,size)(__range_ok(addr,size)==0)
            可以看到access_ok中第一個參數(shù)type并沒有用到,__range_ok的作用在于判斷addr+size之后是否還在進(jìn)程的用戶空間范圍之內(nèi)。下面我們具體看一下。這段代碼涉及到GCC內(nèi)聯(lián)匯編,不懂的朋友可以先看看這篇博客(http://blog.csdn.net/ce123/article/details/8209702)。
            (1)unsigned long flag, sum;\定義兩個變量
            • flag:保存結(jié)果的變量:非零代表地址無效,零代表地址可以訪問。初始存放非零值(current_thread_info()->addr_limit),也就是當(dāng)前進(jìn)程的地址上限值。
            • sum:保存要訪問的地址范圍末端,用于和當(dāng)前進(jìn)程地址空間限制數(shù)據(jù)做比較。
            (2)__chk_user_ptr(addr);\定義是一個空函數(shù)
            這個函數(shù)涉及到__CHECKER__宏的判斷,__CHECKER__宏在通過Sparse(Semantic Parser for C)工具對內(nèi)核代碼進(jìn)行檢查時會定義的。在使用make C=1或C=2時便會調(diào)用該工具,這個工具可以檢查在代碼中聲明了sparse所能檢查到的相關(guān)屬性的內(nèi)核函數(shù)和變量。
            • 如果定義了__CHECKER__,__chk_user_ptr和__chk_io_ptr在這里只聲明函數(shù),沒有函數(shù)體,目的就是在編譯過程中Sparse能夠捕捉到編譯錯誤,檢查參數(shù)的類型。
            • 如果沒有定義__CHECKER__,這就是一個空函數(shù)。

            請看具體的定義(linux/compiler.h):

            [plain]view plaincopy
            print?
            1. #ifdef__CHECKER__
            2. ...
            3. externvoid__chk_user_ptr(void__user*);
            4. externvoid__chk_io_ptr(void__iomem*);
            5. #else
            6. ...
            7. #define__chk_user_ptr(x)(void)0
            8. #define__chk_io_ptr(x)(void)0
            9. ...
            10. #endif
            (3)接下來是匯編:
            adds %1, %2, %3
            sum = addr + size 這個操作影響狀態(tài)位(目的是影響是進(jìn)位標(biāo)志C),以下的兩個指令都帶有條件CC,也就是當(dāng)C=0的時候才執(zhí)行。

            如果上面的加法指令進(jìn)位了(C=1),則以下的指令都不執(zhí)行,flag就為初始值current_thread_info()->addr_limit(非0),并返回。
            如果沒有進(jìn)位(C=0),就執(zhí)行下面的指令:
            sbcccs %1, %1, %0
            sum = sum - flag - 1,也就是(addr + size) - (current_thread_info()->addr_limit) - 1,操作影響符號位。
            如果(addr + size) >= (current_thread_info()->addr_limit) - 1,則C=1
            如果(addr + size) < (current_thread_info()->addr_limit) - 1,則C=0
            當(dāng)C=0的時候執(zhí)行以下指令,否則跳過(flag非零)。
            movcc %0, #0
            flag = 0,給flag賦值0。

            綜上所述:__range_ok宏其實等價于:

            • 如果(addr + size) >= (current_thread_info()->addr_limit) - 1,返回非零值
            • 如果(addr + size) < (current_thread_info()->addr_limit),返回零
            而access_ok就是檢驗將要操作的用戶空間的地址范圍是否在當(dāng)前進(jìn)程的用戶地址空間限制中。這個宏的功能很簡單,完全可以用C實現(xiàn),不是必須使用匯編。但于這兩個函數(shù)使用頻繁,就使用匯編來實現(xiàn)部分功能來增加效率。
            從這里再次可以認(rèn)識到,copy_from_user的使用是結(jié)合進(jìn)程上下文的,因為他們要訪問“user”的內(nèi)存空間,這個“user”必須是某個特定的進(jìn)程。通過上面的源碼就知道,其中使用了current_thread_info()來檢查空間是否可以訪問。如果在驅(qū)動中使用這兩個函數(shù),必須是在實現(xiàn)系統(tǒng)調(diào)用的函數(shù)中使用,不可在實現(xiàn)中斷處理的函數(shù)中使用。如果在中斷上下文中使用了,那代碼就很可能操作了根本不相關(guān)的進(jìn)程地址空間。其次由于操作的頁面可能被換出,這兩個函數(shù)可能會休眠,所以同樣不可在中斷上下文中使用。

            1.2.__arch_copy_from_user

            在深入講解之前,我們先想一個問題:為什么要使用copy_from_user函數(shù)???理論上,內(nèi)核空間可以直接使用用戶空間傳過來的指針,即使要做數(shù)據(jù)拷貝的動作,也可以直接使用memcpy,事實上,在沒有MMU的體系架構(gòu)上,copy_form_user最終的實現(xiàn)就是利用了memcpy。但對于大多數(shù)有MMU的平臺,情況就有了一些變化:用戶空間傳過來的指針是在虛擬地址空間上的,它指向的虛擬地址空間很可能還沒有真正映射到實際的物理頁面上。但這又能怎樣呢?缺頁導(dǎo)致的異常會透明的被內(nèi)核予以修復(fù)(為缺頁的地址空間提交新的物理頁面),訪問到缺頁的指令會繼續(xù)運行仿佛什么都沒有發(fā)生一樣。但這只是用戶空間缺頁異常的行為,在內(nèi)核空間這樣卻因一場必須被顯示的修復(fù),這是由內(nèi)核提供的缺頁異常處理函數(shù)的設(shè)計模式?jīng)Q定的,其背后的思想后:在內(nèi)核態(tài)中,如果程序試圖訪問一個尚未提交物理頁面的用戶空間地址,內(nèi)核必須對此保持警惕而不能像用戶空間那樣毫無察覺。
            如果內(nèi)核訪問一個尚未被提交物理頁面的空間,將產(chǎn)生缺頁異常,內(nèi)核會調(diào)用do_page_fault,因為異常發(fā)生在內(nèi)核空間,do_page_fault將調(diào)用search_exception_tables在“ __ex_table”中查找異常指令的修復(fù)指令,在__arch_copy_from_user函數(shù)中經(jīng)常使用USER宏,這個宏中了定義了“__ex_table”section。
            linux/include/asm-arm/assembler.h
            [plain]view plaincopy
            print?
            1. #defineUSER(x...)
            2. 9999:x;
            3. .section__ex_table,"a";
            4. .align3;
            5. .long9999b,9001f;
            6. .previous
            該定義中有如下數(shù)據(jù);
            [plain]view plaincopy
            print?
            1. .long9999b,9001f;
            其中9999b對應(yīng)標(biāo)號9999處的指令,9001f是9001處的指令,是9999b處指令的修復(fù)指令。這樣,當(dāng)標(biāo)號9999處發(fā)生缺頁異常時,系統(tǒng)將調(diào)用do_page_fault提交物理頁面,然后跳到9001繼續(xù)執(zhí)行。
            如果在驅(qū)動程序中不使用copy_from_user而用memcpy來代替,對于上述的情形會產(chǎn)生什么結(jié)果呢?當(dāng)標(biāo)號9999出發(fā)生缺頁異常時,系統(tǒng)在“__ex_table”section總將找不到修復(fù)地址,因為memcpy沒有像copy_from_user那樣定義一個“__ex_table”section,此時do_page_fault將通過no_context函數(shù)產(chǎn)生Oops。極有可能會看到類似如下信息:
            Unable to handle kernel NULL pointer dereference at virtual address 00000fe0
            所有為了確保設(shè)備驅(qū)動程序的安全,應(yīng)該使用copy_from_user函數(shù)而不是memcpy。
            下面我們深入分析__arch_copy_from_user函數(shù)的實現(xiàn),該函數(shù)是用匯編實現(xiàn)的,定義在linux/arch/arm/lib/uaccess.S文件中。

            [plain]view plaincopy
            print?
            1. /*Prototype:unsignedlong__arch_copy_from_user(void*to,constvoid*from,unsignedlongn);
            2. *Purpose:copyablockfromusermemorytokernelmemory
            3. *Params:to-kernelmemory
            4. *:from-usermemory
            5. *:n-numberofbytestocopy
            6. *Returns:NumberofbytesNOTcopied.
            7. */
            8. .cfu_dest_not_aligned:
            9. rsbip,ip,#4
            10. cmpip,#2
            11. USER(ldrbtr3,[r1],#1)@Mayfault
            12. strbr3,[r0],#1
            13. USER(ldrgebtr3,[r1],#1)@Mayfault
            14. strgebr3,[r0],#1
            15. USER(ldrgtbtr3,[r1],#1)@Mayfault
            16. strgtbr3,[r0],#1
            17. subr2,r2,ip
            18. b.cfu_dest_aligned
            19. ENTRY(__arch_copy_from_user)
            20. stmfdsp!,{r0,r2,r4-r7,lr}
            21. cmpr2,#4
            22. blt.cfu_not_enough
            23. PLD(pld[r1,#0])
            24. PLD(pld[r0,#0])
            25. andsip,r0,#3
            26. bne.cfu_dest_not_aligned
            27. .cfu_dest_aligned:
            28. andsip,r1,#3
            29. bne.cfu_src_not_aligned
            30. /*
            31. *Seeingastherehastobeatleast8bytestocopy,wecan
            32. *copyoneword,andforceauser-modepagefault...
            33. */
            34. .cfu_0fupi:subsr2,r2,#4
            35. addmiip,r2,#4
            36. bmi.cfu_0nowords
            37. USER(ldrtr3,[r1],#4)
            38. strr3,[r0],#4
            39. movip,r1,lsl#32-PAGE_SHIFT@Oneachpage,useald/st??tinstruction
            40. rsbip,ip,#0
            41. movsip,ip,lsr#32-PAGE_SHIFT
            42. beq.cfu_0fupi
            43. /*
            44. *ip=maxno.ofbytestocopybeforeneedinganother"strt"insn
            45. */
            46. cmpr2,ip
            47. movltip,r2
            48. subr2,r2,ip
            49. subsip,ip,#32
            50. blt.cfu_0rem8lp
            51. PLD(pld[r1,#28])
            52. PLD(pld[r0,#28])
            53. PLD(subsip,ip,#64)
            54. PLD(blt.cfu_0cpynopld)
            55. PLD(pld[r1,#60])
            56. PLD(pld[r0,#60])
            57. .cfu_0cpy8lp:
            58. PLD(pld[r1,#92])
            59. PLD(pld[r0,#92])
            60. .cfu_0cpynopld:ldmiar1!,{r3-r6}@Shouldntfault
            61. stmiar0!,{r3-r6}
            62. ldmiar1!,{r3-r6}@Shouldntfault
            63. subsip,ip,#32
            64. stmiar0!,{r3-r6}
            65. bpl.cfu_0cpy8lp
            66. PLD(cmnip,#64)
            67. PLD(bge.cfu_0cpynopld)
            68. PLD(addip,ip,#64)
            69. .cfu_0rem8lp:cmnip,#16
            70. ldmgeiar1!,{r3-r6}@Shouldntfault
            71. stmgeiar0!,{r3-r6}
            72. tstip,#8
            73. ldmneiar1!,{r3-r4}@Shouldntfault
            74. stmneiar0!,{r3-r4}
            75. tstip,#4
            76. ldrnetr3,[r1],#4@Shouldntfault
            77. strner3,[r0],#4
            78. andsip,ip,#3
            79. beq.cfu_0fupi
            80. .cfu_0nowords:teqip,#0
            81. beq.cfu_finished
            82. .cfu_nowords:cmpip,#2
            83. USER(ldrbtr3,[r1],#1)@Mayfault
            84. strbr3,[r0],#1
            85. USER(ldrgebtr3,[r1],#1)@Mayfault
            86. strgebr3,[r0],#1
            87. USER(ldrgtbtr3,[r1],#1)@Mayfault
            88. strgtbr3,[r0],#1
            89. b.cfu_finished
            90. .cfu_not_enough:
            91. movsip,r2
            92. bne.cfu_nowords
            93. .cfu_finished:movr0,#0
            94. addsp,sp,#8
            95. LOADREGS(fd,sp!,{r4-r7,pc})
            96. .cfu_src_not_aligned:
            97. bicr1,r1,#3
            98. USER(ldrtr7,[r1],#4)@Mayfault
            99. cmpip,#2
            100. bgt.cfu_3fupi
            101. beq.cfu_2fupi
            102. .cfu_1fupi:subsr2,r2,#4
            103. addmiip,r2,#4
            104. bmi.cfu_1nowords
            105. movr3,r7,pull#8
            106. USER(ldrtr7,[r1],#4)@Mayfault
            107. orrr3,r3,r7,push#24
            108. strr3,[r0],#4
            109. movip,r1,lsl#32-PAGE_SHIFT
            110. rsbip,ip,#0
            111. movsip,ip,lsr#32-PAGE_SHIFT
            112. beq.cfu_1fupi
            113. cmpr2,ip
            114. movltip,r2
            115. subr2,r2,ip
            116. subsip,ip,#16
            117. blt.cfu_1rem8lp
            118. PLD(pld[r1,#12])
            119. PLD(pld[r0,#12])
            120. PLD(subsip,ip,#32)
            121. PLD(blt.cfu_1cpynopld)
            122. PLD(pld[r1,#28])
            123. PLD(pld[r0,#28])
            124. .cfu_1cpy8lp:
            125. PLD(pld[r1,#44])
            126. PLD(pld[r0,#44])
            127. .cfu_1cpynopld:movr3,r7,pull#8
            128. ldmiar1!,{r4-r7}@Shouldntfault
            129. subsip,ip,#16
            130. orrr3,r3,r4,push#24
            131. movr4,r4,pull#8
            132. orrr4,r4,r5,push#24
            133. movr5,r5,pull#8
            134. orrr5,r5,r6,push#24
            135. movr6,r6,pull#8
            136. orrr6,r6,r7,push#24
            137. stmiar0!,{r3-r6}
            138. bpl.cfu_1cpy8lp
            139. PLD(cmnip,#32)
            140. PLD(bge.cfu_1cpynopld)
            141. PLD(addip,ip,#32)
            142. .cfu_1rem8lp:tstip,#8
            143. movner3,r7,pull#8
            144. ldmneiar1!,{r4,r7}@Shouldntfault
            145. orrner3,r3,r4,push#24
            146. movner4,r4,pull#8
            147. orrner4,r4,r7,push#24
            148. stmneiar0!,{r3-r4}
            149. tstip,#4
            150. movner3,r7,pull#8
            151. USER(ldrnetr7,[r1],#4)@Mayfault
            152. orrner3,r3,r7,push#24
            153. strner3,[r0],#4
            154. andsip,ip,#3
            155. beq.cfu_1fupi
            156. .cfu_1nowords:movr3,r7,get_byte_1
            157. teqip,#0
            158. beq.cfu_finished
            159. cmpip,#2
            160. strbr3,[r0],#1
            161. movger3,r7,get_byte_2
            162. strgebr3,[r0],#1
            163. movgtr3,r7,get_byte_3
            164. strgtbr3,[r0],#1
            165. b.cfu_finished
            166. .cfu_2fupi:subsr2,r2,#4
            167. addmiip,r2,#4
            168. bmi.cfu_2nowords
            169. movr3,r7,pull#16
            170. USER(ldrtr7,[r1],#4)@Mayfault
            171. orrr3,r3,r7,push#16
            172. strr3,[r0],#4
            173. movip,r1,lsl#32-PAGE_SHIFT
            174. rsbip,ip,#0
            175. movsip,ip,lsr#32-PAGE_SHIFT
            176. beq.cfu_2fupi
            177. cmpr2,ip
            178. movltip,r2
            179. subr2,r2,ip
            180. subsip,ip,#16
            181. blt.cfu_2rem8lp
            182. PLD(pld[r1,#12])
            183. PLD(pld[r0,#12])
            184. PLD(subsip,ip,#32)
            185. PLD(blt.cfu_2cpynopld)
            186. PLD(pld[r1,#28])
            187. PLD(pld[r0,#28])
            188. .cfu_2cpy8lp:
            189. PLD(pld[r1,#44])
            190. PLD(pld[r0,#44])
            191. .cfu_2cpynopld:movr3,r7,pull#16
            192. ldmiar1!,{r4-r7}@Shouldntfault
            193. subsip,ip,#16
            194. orrr3,r3,r4,push#16
            195. movr4,r4,pull#16
            196. orrr4,r4,r5,push#16
            197. movr5,r5,pull#16
            198. orrr5,r5,r6,push#16
            199. movr6,r6,pull#16
            200. orrr6,r6,r7,push#16
            201. stmiar0!,{r3-r6}
            202. bpl.cfu_2cpy8lp
            203. PLD(cmnip,#32)
            204. PLD(bge.cfu_2cpynopld)
            205. PLD(addip,ip,#32)
            206. .cfu_2rem8lp:tstip,#8
            207. movner3,r7,pull#16
            208. ldmneiar1!,{r4,r7}@Shouldntfault
            209. orrner3,r3,r4,push#16
            210. movner4,r4,pull#16
            211. orrner4,r4,r7,push#16
            212. stmneiar0!,{r3-r4}
            213. tstip,#4
            214. movner3,r7,pull#16
            215. USER(ldrnetr7,[r1],#4)@Mayfault
            216. orrner3,r3,r7,push#16
            217. strner3,[r0],#4
            218. andsip,ip,#3
            219. beq.cfu_2fupi
            220. .cfu_2nowords:movr3,r7,get_byte_2
            221. teqip,#0
            222. beq.cfu_finished
            223. cmpip,#2
            224. strbr3,[r0],#1
            225. movger3,r7,get_byte_3
            226. strgebr3,[r0],#1
            227. USER(ldrgtbtr3,[r1],#0)@Mayfault
            228. strgtbr3,[r0],#1
            229. b.cfu_finished
            230. .cfu_3fupi:subsr2,r2,#4
            231. addmiip,r2,#4
            232. bmi.cfu_3nowords
            233. movr3,r7,pull#24
            234. USER(ldrtr7,[r1],#4)@Mayfault
            235. orrr3,r3,r7,push#8
            236. strr3,[r0],#4
            237. movip,r1,lsl#32-PAGE_SHIFT
            238. rsbip,ip,#0
            239. movsip,ip,lsr#32-PAGE_SHIFT
            240. beq.cfu_3fupi
            241. cmpr2,ip
            242. movltip,r2
            243. subr2,r2,ip
            244. subsip,ip,#16
            245. blt.cfu_3rem8lp
            246. PLD(pld[r1,#12])
            247. PLD(pld[r0,#12])
            248. PLD(subsip,ip,#32)
            249. PLD(blt.cfu_3cpynopld)
            250. PLD(pld[r1,#28])
            251. PLD(pld[r0,#28])
            252. .cfu_3cpy8lp:
            253. PLD(pld[r1,#44])
            254. PLD(pld[r0,#44])
            255. .cfu_3cpynopld:movr3,r7,pull#24
            256. ldmiar1!,{r4-r7}@Shouldntfault
            257. orrr3,r3,r4,push#8
            258. movr4,r4,pull#24
            259. orrr4,r4,r5,push#8
            260. movr5,r5,pull#24
            261. orrr5,r5,r6,push#8
            262. movr6,r6,pull#24
            263. orrr6,r6,r7,push#8
            264. stmiar0!,{r3-r6}
            265. subsip,ip,#16
            266. bpl.cfu_3cpy8lp
            267. PLD(cmnip,#32)
            268. PLD(bge.cfu_3cpynopld)
            269. PLD(addip,ip,#32)
            270. .cfu_3rem8lp:tstip,#8
            271. movner3,r7,pull#24
            272. ldmneiar1!,{r4,r7}@Shouldntfault
            273. orrner3,r3,r4,push#8
            274. movner4,r4,pull#24
            275. orrner4,r4,r7,push#8
            276. stmneiar0!,{r3-r4}
            277. tstip,#4
            278. movner3,r7,pull#24
            279. USER(ldrnetr7,[r1],#4)@Mayfault
            280. orrner3,r3,r7,push#8
            281. strner3,[r0],#4
            282. andsip,ip,#3
            283. beq.cfu_3fupi
            284. .cfu_3nowords:movr3,r7,get_byte_3
            285. teqip,#0
            286. beq.cfu_finished
            287. cmpip,#2
            288. strbr3,[r0],#1
            289. USER(ldrgebtr3,[r1],#1)@Mayfault
            290. strgebr3,[r0],#1
            291. USER(ldrgtbtr3,[r1],#1)@Mayfault
            292. strgtbr3,[r0],#1
            293. b.cfu_finished
            294. .section.fixup,"ax"
            295. .align0
            296. /*
            297. *Wetookanexception.r0containsapointerto
            298. *thebytenotcopied.
            299. */
            300. 9001:ldrr2,[sp],#4@void*to
            301. subr2,r0,r2@bytescopied
            302. ldrr1,[sp],#4@unsignedlongcount
            303. subsr4,r1,r2@byteslefttocopy
            304. movner1,r4
            305. blne__memzero
            306. movr0,r4
            307. LOADREGS(fd,sp!,{r4-r7,pc})
            308. .previous
            我們將在另一篇博文中詳細(xì)分析該函數(shù)。


            評論


            技術(shù)專區(qū)

            關(guān)閉