自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

從單片機(jī)步入Linux之運(yùn)行地址與加載地址

系統(tǒng) Linux
在玩單片機(jī)(以stm32為例)的時(shí)候會(huì)有RAM空間和ROM空間,RAM空間主要是用于數(shù)據(jù)的訪問(wèn),而ROM空間用于存放燒錄的固件,當(dāng)然固件也可以直接加載到RAM中運(yùn)行,只是說(shuō)每次上電都需要重新加載。

[[409220]]

大家好,我是情報(bào)小哥!

本文為【單片機(jī)步入嵌入式Linux】系列文章的第二篇,主要是跟大家講解一下鏈接過(guò)程中幾個(gè)地址的區(qū)分與理解~

01單片機(jī)存儲(chǔ)分配

在玩單片機(jī)(以stm32為例)的時(shí)候會(huì)有RAM空間和ROM空間,RAM空間主要是用于數(shù)據(jù)的訪問(wèn),而ROM空間用于存放燒錄的固件,當(dāng)然固件也可以直接加載到RAM中運(yùn)行,只是說(shuō)每次上電都需要重新加載。

如上圖所示ROM為FLASH地址,而RAM為SRAM地址,毋庸置疑生成的單片機(jī)固件會(huì)燒錄到Flash上,這樣才能保證每次上電都有可以正常運(yùn)行。

對(duì)于很多初學(xué)者該有疑問(wèn)了,明明全局變量等等都是分配到RAM上的呀,怎么說(shuō)固件放到Flash上的呢?

其實(shí)并不矛盾,程序指令中訪問(wèn)變量都是訪問(wèn)變量的地址也就是內(nèi)存的地址,所謂的分配到RAM上,僅僅只是說(shuō)相應(yīng)的變量占據(jù)了對(duì)應(yīng)的RAM地址,并不能理解為這個(gè)變量存在于RAM里面。

可能你還會(huì)繼續(xù)問(wèn) : 暫且認(rèn)同上面的說(shuō)法,那對(duì)這些變量的初值該如何解釋呢?

可以肯定的是,這些全局變量的初值并不是來(lái)源于RAM,因?yàn)镽AM掉完電以后數(shù)據(jù)就丟失了,而在程序正常運(yùn)行過(guò)程中,不管怎么上下電其初值都是我們程序中規(guī)定的,也就是在編譯中確定的。

所以這些初值要保存只可能存在ROM中,這中間肯定有這樣一種機(jī)制 : 在上電以后把ROM中存儲(chǔ)的這些變量初值來(lái)重新初始化到對(duì)應(yīng)的RAM地址,以便后續(xù)程序指令訪問(wèn),這種機(jī)制通常叫分散加載。

02簡(jiǎn)述分散加載

上圖是一種簡(jiǎn)單的分散加載機(jī)制,映像文件由不同的段組成,通常都有代碼段(.text)、已初始化數(shù)據(jù)段(.data)、未初始化及初始化為0的數(shù)據(jù)段(.bss)等等,而且他們具有不同的屬性RO,RW,ZI等等。

為了便于大家理解,整個(gè)系統(tǒng)的存儲(chǔ)區(qū)分為ROM和SRAM,左邊Load View表示的是程序存儲(chǔ)地址空間分布情況,也就是程序燒錄到ROM以后的空間分配情況。

固件燒錄到ROM區(qū)域并且分為RW區(qū)和RO區(qū),RW區(qū)域?yàn)榭勺x可寫(xiě)區(qū)域而RO區(qū)域?yàn)橹蛔x區(qū),分這兩個(gè)區(qū)域并不是說(shuō)RW區(qū)域存儲(chǔ)地址區(qū)域以后就用來(lái)數(shù)據(jù)的讀寫(xiě),而是為了上電過(guò)程中的copy/decompress(復(fù)制或者解壓)過(guò)程做好標(biāo)記,這個(gè)過(guò)程會(huì)把一些非零全局變量(或者靜態(tài)變量等)的SRAM地址(實(shí)際的運(yùn)行地址)處賦予初始值。

ZI區(qū)域是零填充區(qū)域,主要是.bss段的一些初始化為0或者未初始化的全局或者靜態(tài)變量分布區(qū)域,這些數(shù)據(jù)沒(méi)有必要保存到固件中,所以由加載機(jī)制自行清零即可。

一切準(zhǔn)備就緒就形成了右側(cè)的execution View的運(yùn)行空間視野,由于ROM中程序運(yùn)行所涉及到的全局變量等的訪問(wèn)都是SRAM地址的訪問(wèn),而這些地址恰好在程序編譯鏈接過(guò)程中已經(jīng)分配到SRAM里面,經(jīng)過(guò)前面的該部分地址的重新定位,運(yùn)行空間的程序就可以正確訪問(wèn)到這些變量的初值等等。

03stm32啟動(dòng)流程

很多剛玩MCU的朋友,都會(huì)以main函數(shù)作為程序的開(kāi)始運(yùn)行處,不過(guò)幾乎所有的C程序在執(zhí)行前都會(huì)使用匯編指令,通過(guò)匯編指令構(gòu)建C語(yǔ)言運(yùn)行環(huán)境,并運(yùn)行C程序,所以在C程序執(zhí)行前做了非常多的工作,其中非常重要的就是堆棧指針的設(shè)置,這也是從匯編到C運(yùn)行環(huán)境一定要做的一件事了。

那么stm32的啟動(dòng)大致流程是怎樣的?這里小哥就簡(jiǎn)述一下:

當(dāng)然還有一些小細(xì)節(jié),這里就不展開(kāi)了,stm32的Flash可以直接運(yùn)行程序,采用分散加載,只需要把相應(yīng)的數(shù)據(jù)區(qū)域加載到運(yùn)行地址處便可以正常的訪問(wèn),這個(gè)與前面的所說(shuō)是類(lèi)似的。

04uboot部署Linux

在進(jìn)行Linux系統(tǒng)開(kāi)發(fā)過(guò)程中,一切從Bootloader開(kāi)始,而bootloader本質(zhì)上就是一個(gè)單任務(wù)的裸機(jī)程序,和單片機(jī)程序是一樣的,而在眾多bootloader中最為常用和廣泛的就是uboot了,他就是為了部署Linux環(huán)境而生的,下載、燒錄、運(yùn)行Linux映像、文件系統(tǒng)等等。

uboot都可以搞定,所以它對(duì)地址是非常敏感的,程序、參數(shù)等等應(yīng)該存儲(chǔ)在什么地址,在什么地方運(yùn)行都是需要確定好的,而這些地址在編譯鏈接的過(guò)程中,鏈接腳本已經(jīng)確定好了這一切,uboot的工作就是把這些固件放在編譯鏈接所規(guī)定的運(yùn)行地址處進(jìn)行運(yùn)行即可。

圖片

比如全局變量在什么地址,函數(shù)在什么地址,當(dāng)程序運(yùn)行的過(guò)程中就會(huì)從這些確切的地址處取數(shù)據(jù),如果你把全局函數(shù)指針變量的地址分配到了NANDFlash上,那么程序在訪問(wèn)的過(guò)程中就有可能跑飛。

程序運(yùn)行最重要的兩個(gè)地址加載地址和運(yùn)行地址。

加載地址也常被大家成為存儲(chǔ)地址,即實(shí)際固件存儲(chǔ)的位置,其實(shí)該地址也只是一個(gè)相對(duì)的概念,就相當(dāng)于單片機(jī)中bin文件燒錄在什么位置一樣的道理。

運(yùn)行地址也叫鏈接地址,即程序的絕對(duì)地址。全局變量等等都是以該地址為基礎(chǔ),來(lái)確定程序的運(yùn)行狀態(tài)的各部分的地址布局。

當(dāng)然Linux以上各部分直接燒寫(xiě)到RAM也是也可以直接運(yùn)行的,不過(guò)還是那個(gè)問(wèn)題,一旦掉電則全部丟失,所以最終每個(gè)部分都會(huì)寫(xiě)入到Flash上(當(dāng)然在前期調(diào)試的時(shí)候可以直接下載到RAM中,減少對(duì)Flash的反復(fù)擦寫(xiě)),但對(duì)于大部分Flash都是無(wú)法直接運(yùn)行程序的,即使能夠運(yùn)行,比如Norflash也是非常的慢,且不能夠直接寫(xiě)入,所以Linux內(nèi)核等都會(huì)加載到RAM來(lái)運(yùn)行,以獲得更快的執(zhí)行速度,那么前面介紹的那種單片機(jī)方式只重定位數(shù)據(jù)段的方式不太適用了。

在嵌入式Linux平臺(tái)上,首先執(zhí)行的就是bootloader,而它只是一個(gè)順序執(zhí)行的程序,它有一個(gè)重要的工作就是把Linux內(nèi)核搬運(yùn)到RAM中運(yùn)行,由于我們的內(nèi)核兼容不同的單板,uboot也會(huì)傳遞給內(nèi)核一些配置參數(shù)以配置內(nèi)核。

往往RAM分配的地址比較高,而整個(gè)程序往往都是0地址開(kāi)始執(zhí)行了的,如果讓存儲(chǔ)地址與運(yùn)行地址相同來(lái)進(jìn)行編譯,會(huì)導(dǎo)致最終燒錄文件非常之大,并且中間有一大片地址區(qū)域是無(wú)效的。

那么有什么辦法來(lái)解決這個(gè)無(wú)效區(qū)域以縮小我們的固件大小呢?先了解下位置無(wú)關(guān)指令。

05位置無(wú)關(guān)指令

既然有位置無(wú)關(guān)指令就有位置有關(guān)指令,簡(jiǎn)單的說(shuō)所執(zhí)行的指令是不是與位置相關(guān)才能達(dá)到目的。

可以類(lèi)比與絕對(duì)路徑與相對(duì)路徑,相對(duì)路徑你可以把程序放在任何文件夾下面,編輯器均可以根據(jù)工程文件路徑找到其他每一個(gè)文件,而絕對(duì)路徑卻不行,一旦文件夾換了,基本上就是定位不到具體的每個(gè)文件了。

所以位置無(wú)關(guān)就相當(dāng)于相對(duì)路徑,數(shù)據(jù)的訪問(wèn)、函數(shù)的調(diào)用幾乎都是相對(duì)的,為什么說(shuō)是幾乎呢?因?yàn)橛行┣闆r下訪問(wèn)絕對(duì)地址也是與位置關(guān)系不大的,可以把這段程序放在可以執(zhí)行的任何位置,所以位置無(wú)關(guān)碼的運(yùn)行與鏈接地址也沒(méi)有直接的聯(lián)系。

比如跳轉(zhuǎn)指令B BL等這些跳轉(zhuǎn)指令采用PC+偏移量,所以為位置無(wú)關(guān)指令;而如果我們采用ldr r0, =標(biāo)記,而這些標(biāo)記都是實(shí)際在鏈接過(guò)程中確定的運(yùn)行地址,所以該指令為位置有關(guān)指令;并且全局變量基本上都是位置有關(guān),而局部變量為位置無(wú)關(guān);所以對(duì)于位置無(wú)關(guān)代碼區(qū)域,跳轉(zhuǎn)一般都使用B指令,而從位置無(wú)關(guān)代碼區(qū)域跳轉(zhuǎn)到位置有關(guān)指令代碼區(qū)域去執(zhí)行就需要借助位置有關(guān)跳轉(zhuǎn)指令。

06加載與運(yùn)行地址不同

當(dāng)存儲(chǔ)地址與鏈接地址不同時(shí),多數(shù)情況下由于采用位置有關(guān)指令會(huì)出問(wèn)題,最常見(jiàn)的就是PC指針取的絕對(duì)地址,而此時(shí)該絕對(duì)地址處無(wú)存儲(chǔ),導(dǎo)致程序飛掉。

既然有了位置無(wú)關(guān)的程序,那么我們就可以把其當(dāng)作一個(gè)搬運(yùn)工放在位置有關(guān)部分的后面,一旦需要運(yùn)行位置有關(guān)碼,那么就會(huì)通過(guò)位置無(wú)關(guān)碼把有關(guān)部分拷貝到運(yùn)行地址處,然后跳轉(zhuǎn)執(zhí)行即可,這樣整個(gè)的程序就可以做得非常的連續(xù)且中間幾乎沒(méi)有無(wú)效區(qū)域,該搬運(yùn)的過(guò)程就是常說(shuō)的重定位。

07地址的設(shè)置

大部分ARM處理器其PC都是從0地址開(kāi)始執(zhí)行,所以在0地址處要么是運(yùn)行程序,要么就是引導(dǎo)程序,如果沒(méi)有這兩樣,你的程序燒錄到其他位置均無(wú)法得到運(yùn)行。

對(duì)于S3C2440芯片能夠支持NorFlash和NandFlash啟動(dòng),其中NorFlash上可以直接運(yùn)行,而NandFlash啟動(dòng)由于其程序無(wú)法直接在上面運(yùn)行,芯片會(huì)把內(nèi)部SRAM作為0地址處,并且把NandFlash前4K代碼拷貝到SRAM上運(yùn)行。

因?yàn)檫@里最終想讓所有的程序都在SDRAM里面運(yùn)行,考慮使用全部重定位的辦法,在鏈接腳本中確定好程序的存儲(chǔ)地址和運(yùn)行地址。

上圖是GUN linker中截取的段描述格式,來(lái)源于:

http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html

具體詳細(xì)解讀大家可以參考上面的鏈接,下面看看幾個(gè)常用的。

可執(zhí)行文件由各個(gè)段組成,:

1、secname段名,一般使用數(shù)據(jù)段.data段,代碼段.text段等等。

2、AT(ldadr)表示該段存儲(chǔ)地址,也就是加載地址。

3、contents表示目標(biāo)文件(比如.o目標(biāo)文件)中的哪些段放在本段,也可以是整個(gè)目標(biāo)文件全部放在這個(gè)段內(nèi)。

4、start表示本段鏈接(或者稱(chēng)為運(yùn)行)的地址,如果沒(méi)有使用AT(ldadr),本段存儲(chǔ)的地址也是start,也就是說(shuō)存儲(chǔ)地址與運(yùn)行地址相等。

通過(guò)上面的段描述格式就可以在鏈接過(guò)程中確定好程序的運(yùn)行地址和載入地址,以方便后續(xù)的重定位地址的使用。

下面以一個(gè)簡(jiǎn)單的實(shí)例說(shuō)明一下:

  1.  1//....格式 
  2.  2SECTIONS { 
  3.  3... 
  4.  4secname start BLOCK(align) (NOLOAD) : AT ( ldadr ) 
  5.  5  { contents } >region :phdr =fill 
  6.  6... 
  7.  7} 
  8.  8//.....示例 
  9.  9SECTIONS { 
  10. 10... 
  11. 11.text 0x30000 : AT ( 0x0000 ) 
  12. 12  { *(.text) } 
  13. 13 
  14. 14.data 0x3FFFF : AT ( 0xFFFF ) 
  15. 15  { *(.data) } 
  16. 16... 
  17. 17} 

這樣固件的代碼段的存儲(chǔ)地址為0,數(shù)據(jù)段存儲(chǔ)地址為0xFFFF,而運(yùn)行地址分別為0x30000和0x3FFFF,最終重定位部分就根據(jù)這鏈接腳本中的符號(hào)獲得相應(yīng)地址,然后把相應(yīng)的部分"搬運(yùn)"到運(yùn)行地址處運(yùn)行處,比如如果載入地址在NandFlash上,那么重定位的過(guò)程中就需要初始化NandFlash控制器,然后讀取NandFlash上的數(shù)據(jù)并"搬運(yùn)"到運(yùn)行地址處。

在嵌入式linux中很多時(shí)候這些地址都需要我們自己確認(rèn)和設(shè)置的,不然Linux內(nèi)核無(wú)法啟動(dòng)或者加載相應(yīng)程序,而在單片機(jī)開(kāi)發(fā)中用慣了IDE工具,所以大部分人涉及得不多~

本文轉(zhuǎn)載自微信公眾號(hào)「嵌入式情報(bào)局」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系嵌入式情報(bào)局公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 嵌入式情報(bào)局
相關(guān)推薦

2021-07-08 09:15:20

單片機(jī)編程狀態(tài)機(jī)編程語(yǔ)言

2021-10-14 15:48:28

鴻蒙HarmonyOS應(yīng)用

2021-06-22 10:02:07

單片機(jī)語(yǔ)言代碼

2021-11-08 10:53:58

IPMAC地址

2013-01-04 10:31:32

單片機(jī)網(wǎng)絡(luò)交換機(jī)

2010-09-08 15:18:54

單片機(jī)TCP IP協(xié)議棧

2009-04-11 15:12:24

2010-06-19 13:32:36

TCP IP協(xié)議棧

2010-06-19 14:10:35

TCP IP協(xié)議棧

2020-12-03 06:32:21

STM32單片機(jī)通信

2022-01-26 08:31:25

聯(lián)合體單片機(jī)編程

2011-05-24 17:47:40

2021-01-08 05:59:39

Linux應(yīng)用程序Linux系統(tǒng)

2022-03-01 08:31:37

volatile變量編譯器

2010-07-01 15:38:41

TCP IP協(xié)議棧單片機(jī)

2018-05-18 09:07:43

Linux內(nèi)核內(nèi)存

2009-04-22 17:18:29

PCB技術(shù)單片機(jī)

2010-08-20 09:00:42

控制列表

2024-02-21 23:16:08

C語(yǔ)言開(kāi)發(fā)

2025-04-16 08:15:00

網(wǎng)絡(luò)通信IP地址網(wǎng)絡(luò)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)