在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux內核編譯與啟動分析

汽車電子技術 ? 來源:宅學部落 ? 作者:王利濤 ? 2023-02-17 09:43 ? 次閱讀

Linux環境下,我們想運行一個應用程序,在shell交互環境下直接敲命令就可以了,操作系統給程序提供了運行環境和進程管理。那Linux操作系統本身是如何運行和啟動的呢?在分析之前,我們先做一個Linux內核啟動的實驗:通過u-boot加載Linux內核鏡像uImage到內存不同地址,觀察Linux內核啟動流程。

實驗環境:

  • 硬件平臺:使用 QEMU 仿真ARM vexpress A9 開發板
  • RAM大小配置:512 MB
  • RAM內存地址:0x60000000 ~ 0x7FFFFFFF

實驗過程:

  • 編譯內核鏡像,將uImage加載地址設置為0x60003000,編譯生成uImage
  • 將內核加載到0x60003000地址,然后bootm 0x60003000
  • 將內核加載到0x60004000地址 ,然后bootm 0x60004000

通過實驗我們可以看到:雖然 uImage 被U-boot加載到了內存 0x60003000 和 0x60004000 內存不同地址,但是通過U-boot的bootm命令都可以正常引導和啟動運行。bootm到底有什么魔法,即使我們把鏡像文件加載到了未指定的內存地址,也能讓Linux神奇般地啟動起來呢?要想一探究竟,還得溯本求源:從Linux內核的編譯鏈接說起。我們從編譯Linux內核鏡像 uImage 的Log信息為切入點分析:

$ make uImage LOADADDR=0x60003000
  CC arch/arm/mm/mmu.o //上面省略的是編譯過程:將.c編譯為.o文件//前方高能預警
  LD      vmlinux
  SYSMAP  System.map
  OBJCOPY arch/arm/boot/Image
  Kernel: arch/arm/boot/Image is ready
  Kernel: arch/arm/boot/Image is ready
  LDS     arch/arm/boot/compressed/vmlinux.lds
  AS      arch/arm/boot/compressed/head.o
  GZIP    arch/arm/boot/compressed/piggy.gzip
  AS      arch/arm/boot/compressed/piggy.gzip.o
  CC      arch/arm/boot/compressed/misc.o
  CC      arch/arm/boot/compressed/decompress.o
  LD      arch/arm/boot/compressed/vmlinux
  OBJCOPY arch/arm/boot/zImage
  Kernel: arch/arm/boot/zImage is ready
  Kernel: arch/arm/boot/Image is ready
  Kernel: arch/arm/boot/zImage is ready
  UIMAGE  arch/arm/boot/uImage
Image Name:   Linux-4.4.0+
Created:      Fri Apr 24 19:11:09 2020
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    3460776 Bytes = 3379.66 kB = 3.30 MB
Load Address: 60003000
Entry Point:  60003000
Image arch/arm/boot/uImage is ready

編譯Linux內核鏡像整個過程比較漫長,大概需要5分鐘左右,并有大量的編譯信息打印出來。前期的打印信息比較簡單,就是分別使用編譯器和匯編器將對應的.c文件、.S文件編譯成 .o 格式可重定位目標文件。真正高能核心的過程在最后的鏈接和鏡像文件格式處理部分,編譯信息已經截取如上。

圖片

結合編譯信息和上面的編譯流程圖我們可以看到,編譯器將所有的源文件編譯成對應的目標文件后,接下來就是鏈接過程:將所有的目標文件鏈接成ELF格式的可執行文件:vmlinux。ELF文件格式是Linux環境下的可執行文件格式,無論是 gcc 還是 arm-linux-gcc 編譯器,生成的都是ELF這種格式的文件。在Linux環境下,加載器根據ELF文件里的地址信息,就可以把它加載到內存指定的地址運行,但是系統啟動過程中并沒有ELF文件的執行環境,需要將ELF文件轉換為二進制純指令文件。編譯器接著會調用objdump命令刪除不必要的section,只保留代碼段、數據段等必要的section,將ELF格式的vmlinux文件轉換為原始的二進制內核鏡像Image。Image可以在裸機環境下運行,體積也比較大,我們可以使用gzip工具對其進行壓縮,生成piggz.gzip壓縮的二進制內核鏡像。這樣做的好處是可提高程序的啟動速度:因為內核加載運行時,從Flash 上讀取鏡像的速度是很慢的,我們通過先壓縮,加載到內存后再解壓這種操作,不僅可以節省Flash存儲空間(尤其是Nor Flash還是很貴的),還可以節省了鏡像的加載時間。

因為piggz.gzip是壓縮文件無法運行,所以我們還需要給它鏈接上一段解壓縮代碼。鏈接器只能處理ELF格式的目標文件,因此在鏈接之前,要先將壓縮文件piggz.gzip轉換為可重定位的目標文件:piggy.gzip.o。在ARM平臺下,解壓縮代碼是在arch/arm/boot/compressed/目錄下面的head.o、misc.o、 decompress.o,這部分使用 -fpic 參數編譯生成的指令是與位置無關的,放到哪里都可以執行,它們通過鏈接器與piggy.gzip.o一起組裝成新的ELF文件vmlinux,然后再使用objcopy工具轉換為純二進制鏡像zImage,就可以直接燒寫到Nor或nand flash上,隨系統啟動后加載到內存運行了。

不同的嵌入式系統平臺可能會使用不同的BootLoader來加載Linux內核鏡像的運行,常見的BootLoader有U-boot、vivi、g-bios等。使用U-boot的嵌入式平臺通常會對zImage進一步轉換,給它添加一個64字節的數據頭,用來記錄鏡像文件的加載地址、入口地址、文件大小、CPU架構等信息。我們可以使用U-boot提供的mkimage工具將zImage鏡像轉換為uImage:

$ mkimageA arm  -O linuxT kernelC nonea 0x60003000e 0x60003000  -d zImage    uImage

mkimage工具常見的參數說明如下:

  • -A:指定CPU架構類型
  • -O:指定操作系統類型
  • -T:指定image類型
  • -C:采用的壓縮方式:none、gzip、bzip2等
  • -a:內核加載地址
  • -e:內核鏡像入口地址

走到這一步,U-boot可以引導的uImage內核鏡像生成,這個Linux內核鏡像編譯就完美結束了。接下來我們繼續分析U-boot是如何加載uImage運行的:

圖片

U-boot加載的 dtb 文件和 bootargs 這里暫不考慮,我們重點關注uImage:當uImage被加載到內存不同的位置時,為什么都可以正常啟動。我們先考慮上面的第一種情況,當加載到內存中的地址等于編譯時指定的地址時:

圖片

U-boot提供的bootm機制用來啟動內核的運行。bootm會解析uImage文件64字節的數據頭,解析出指定的加載地址,并跟自己的參數進行對比:若發現bootm參數地址和編譯時-a指定的加載地址0x60003000相同,就會直接跳過數據頭,跳到zImage的入口地址0x60003040執行。

圖片

如果bootm發現自己的參數地址跟-a指定的加載地址0x60003000不同時,它會將去掉64個字節數據頭的內核鏡像zImage復制到編譯時 -a 指定的加載地址處,然后再跳到該地址處執行。如上圖所示,zImage鏡像被加載到了編譯時指定的0x60003000地址處,然后跳過來,就可以直接執行zImage了。

圖片

zImage是一個壓縮文件,在運行之前要先解出真正要執行的內核鏡像Image,然后才能跳到內核鏡像真正的入口處去啟動Linux內核。解壓縮代碼head.o、decompress.o是一段與位置無關的代碼,放到內存的任何位置都可以運行。大家有興趣可以做一個實驗,使用U-boot的bootz命令直接引導內核鏡像zImage運行:將zImage加載到內存的不同地址,你會發現zImage都可以正常啟動。

圖片

解壓縮代碼的主要作用就是將從zImage文件出解壓出真正的內核鏡像Image,并將其重定位到Image內核編譯時指定的鏈接地址0x80008000上。Linux運行使用的是虛擬地址,需要CPU硬件管理單元MMU的支持,MMU會將虛擬地址轉換為對應的物理地址。在ARM vexpress平臺上,內核的鏈接地址0x80008000會映射到物理內存0x60008000的地方。zImage的解壓縮代碼會將Image解壓到0x60008000處,然后跳過去就可以直接啟動Linux內核了。

圖片

在zImage運行解壓縮代碼的過程中會遇到這么一種情況:zImage自身剛好占據了0x60008000這片地址空間,那么當zImage的重定位代碼將解壓出來的Image拷貝到指定的0x60008000處時,可能就會沖掉自身正在運行的代碼。為了避免這種情況發生,zImage會將這部分重定位拷貝到一個安全的地方,比如Image的后面,然后再跳到這片重定位代碼處執行,這樣就可以將Image鏡像安全地拷貝到0x60008000地址上了。

拷貝成功后,就可以直接跳到 0x60008000 地址去運行Linux內核真正的代碼了。因為Image鏡像鏈接時使用的是虛擬地址,所以在運行Linux內核的C語言函數之前,首先會有一段匯編代碼用來初始化堆棧環境,使能MMU。代碼跟蹤就不具體分析了,有興趣大家可以去看視頻教程:《C語言嵌入式Linux高級編程》第3期:程序的編譯、鏈接和運行,或者參考下面的提示自行分析:

  • 運行入口:arch/arm/kernel/head.S
  • 使能MMU:__create_page_tables
  • 跳入C語言函數:__mmap_switched/start_kernel
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11345

    瀏覽量

    210401
  • 應用程序
    +關注

    關注

    38

    文章

    3292

    瀏覽量

    57917
  • Shell
    +關注

    關注

    1

    文章

    366

    瀏覽量

    23448
收藏 人收藏

    評論

    相關推薦

    Linux編譯驅動、內核及應用程序分析

    作為一名嵌入式Linux新手,在學習的過程中會遇到很多問題。寫了一個驅動程序怎么編譯?怎么加載進內核
    的頭像 發表于 01-17 13:46 ?6706次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>編譯</b>驅動、<b class='flag-5'>內核</b>及應用程序<b class='flag-5'>分析</b>

    Linux內核編譯主要過程

    Linux內核編譯主要過程: 配置、編譯、安裝 。
    發表于 08-08 16:02 ?766次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>的<b class='flag-5'>編譯</b>主要過程

    linux內核編譯

    Linux內核編譯與安裝1從www.kernel.org 下載內核linux-2.6.29.1.tar.bz2包解壓。并執行清理命令mak
    發表于 10-26 14:14

    Linux內核編譯啟動的相關資料分享

    Linux環境下,我們想運行一個應用程序,在shell交互環境下直接敲命令就可以了,操作系統給程序提供了運行環境和進程管理。那Linux操作系統本身是如何運行和啟動的呢?在分析之前,
    發表于 12-20 06:28

    交叉編譯linux內核(raspberry_3.6.y)

    一步一步教你交叉編譯linux內核,RPI的內核編譯教程,小眾的東西了
    發表于 11-03 17:58 ?0次下載

    linux內核啟動內核解壓過程分析

    linux啟動內核解壓過程分析,一份不錯的文檔,深入了解內核必備
    發表于 03-09 13:39 ?1次下載

    Linux內核編譯詳談

    Linux內核編譯詳談
    發表于 10-30 09:51 ?7次下載
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b><b class='flag-5'>編譯</b>詳談

    基于Arm的Linux內核編譯指導

    基于Arm的Linux內核編譯指導
    發表于 10-30 10:13 ?15次下載
    基于Arm的<b class='flag-5'>Linux</b><b class='flag-5'>內核</b><b class='flag-5'>編譯</b>指導

    Linux內核配置編譯分析的設計方案

    Linux內核配置編譯分析的設計方案
    發表于 07-08 16:53 ?18次下載
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>配置<b class='flag-5'>編譯</b><b class='flag-5'>分析</b>的設計方案

    如何才能編譯Linux內核

    內核的bug ,并增加了許多新的特性。如果用戶想要使用這些新特性, 或想根據自己的系統度身定制一個更高效, 更穩定的內核, 就需要重新編譯內核。本文將以RedHat
    發表于 11-04 18:04 ?8次下載

    動手編譯Linux內核的教程免費下載

    本文檔的主要內容詳細介紹的是動手編譯Linux內核的教程免費下載。
    發表于 11-26 17:01 ?14次下載

    Linux內核編譯與運行

    本文檔的主要內容詳細介紹的是Linux內核編譯與運行免費下載。
    發表于 03-25 13:48 ?16次下載

    嵌入式Linux內核編譯

    實驗環境VMware Workstation PlayerUbuntu16.04kernel-3.2.tar.bz2Linux內核編譯在ubuntu上編譯嵌入式
    發表于 11-01 17:07 ?19次下載
    嵌入式<b class='flag-5'>Linux</b>的<b class='flag-5'>內核</b><b class='flag-5'>編譯</b>

    Linux內核編譯和運行

    想讓Linux內核代碼跑起來,得先搭建編譯和運行代碼的環境。
    發表于 06-23 11:56 ?1626次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>的<b class='flag-5'>編譯</b>和運行

    Linux內核編譯腳本

    獲得編譯命令及選項 編譯linux時,默認不會顯示編譯的命令,如果你要獲得編譯命令及其選項,可以在make命令后面加上宏定義: make V
    的頭像 發表于 09-27 11:52 ?688次閱讀
    主站蜘蛛池模板: 最新合集丨新片速递 | 午夜肉伦伦影院在线观看 | 午夜欧美成人久久久久久 | 男男失禁play 把尿bl | 在线网站黄 | 久久综合久色欧美婷婷 | 午夜视频免费在线观看 | 亚洲 欧美 另类 综合 日韩 | 你懂的网址在线 | 香淫 | 男女视频免费 | brazzersvideosex欧美高清 | 色播视频在线观看免费 | 精品国产高清在线看国产 | q2002在线观看免费 | 日韩免费高清一级毛片在线 | 丁香四月婷婷 | 国产成人精品视频一区二区不卡 | 私色综合网 | 视频亚洲一区 | 凸输偷窥xxxx自由视频 | 午夜亚洲福利 | 国产午夜精品福利久久 | www.亚洲综合 | 看黄色一级毛片 | 日本特黄特色免费大片 | 久久五月女厕所一区二区 | 性欧美成人免费观看视 | 日本黄页在线观看 | 永久免费看的啪啪网站 | 高清配种视频xxxxx | 国模掰开 | 免费一级欧美片在线观看 | 夜天干天干啦天干天天爽 | 天天爽天天干 | 亚洲网在线| 四虎在线播放免费永久视频 | 天天色天天摸 | 伊人网址 | 亚洲日韩色综合视频 | 久久免费公开视频 |