從原始碼構建一個極簡的Linux作業系統
現代生活環境中,各式各樣的電子裝置包圍著我們每個人。但凡稍微複雜一點的裝置,比如一臺電視機、路由器或智慧手機,它很有可能執行的就是Linux作業系統,這種想法一直在我腦海出現。
讓我更加困惑的是,在這些裝置或伺服器上執行的Linux作業系統的核心居然都是由同一個原始碼構建而成的,這些原始碼被放在了一個叫做kernel.org網站的倉庫中。
如此多樣的裝置上執行的作業系統都是由相同的原始碼組合而成的!當然,這種說法有點片面,實際上核心通常由特定Linux發行版的開發人員以及特定裝置的開發人員擴充套件和修改的,但核心中卻有很多通用的原始碼。
我一直想靠自己從原始碼開始構建一個Linux作業系統,但過程實在是太複雜,而且越做越亂,究其原因是我有很多沒有掌握的領域。終於在某一時刻,我積累了足夠的知識,現在我可以實現我的夢想了。因此,在這篇文章中,我將展示如何在計算機上從原始碼編譯和執行一個極簡的Linux。
雖然它不會具備所有的功能,但它擁有最重要的東西——命令列介面。相信我,在真正的計算機上獲得一個可以工作的Linux命令列介面將是一種不可思議的體驗。
令人驚訝的是,獲得Linux命令列所需的最小集合只有兩個檔案:Linux核心檔案和根目錄檔案系統初始映象檔案。顯然,這需要一個引導裝載程式來載入這兩個檔案並啟動核心的執行,必要時還可以向它傳遞初始根目錄檔案系統的映象和其他引數。
為了能以某種方式使用此作業系統,你需要四個元件:
載入程式(Bootloader):是一個特殊的程式,它允許處理器去執行位於作業系統核心檔案中的機器指令。
核心(Kernel):此程式的程式碼包含:
用於處理器可以使用的各種物理I/O裝置(裝置驅動程式)的抽象;
用於儲存的資料結構的抽象(檔案系統);
程式指令(程式、執行緒)的時間分離抽象;
其他抽象。
由於核心的存在,應用程式的開發人員通常不關心計算機上安裝了哪種顯示卡、鍵盤或硬碟。他們只編寫與I/O裝置、程式、檔案、套接字等工作的程式碼。
初始根目錄檔案系統(Initial root filesystem):此檔案系統是必須的,以便核心能夠在引導作業系統啟動時載入所必須的檔案。其中最重要的就是Linux核心模組,這些模組沒有包含在核心檔案中,而是用於進一步載入,以及在作業系統啟動時建立第一個程式檔案(init)。
作業系統核心介面的一組實用程式集:它允許我們使用在作業系統核心中發現的抽象。根據作業系統所執行的裝置的複雜性和用途的不同,這個集合也有所不同。這些實用程式定義功能和使用者介面。例如,路由器會有一個程式;手機會有另一個程式;個人電腦還會有一個程式。
在不同的架構和計算機上,Linux作業系統的引導都可能不同,但對於x86架構的計算機,大多數情況下它引導是這樣的:
電腦開機。
BIOS或UEFI在計算機上找到作業系統載入程式,並向其傳輸控制指令。
作業系統引導裝載程式將Linux核心檔案和初始檔案系統映象檔案(initrd檔案)載入到RAM中。
作業系統引導裝載程式將控制傳輸到Linux作業系統核心。
作業系統核心執行初始化。
作業系統核心訪問初始檔案系統映象中的檔案(載入映象)。
核心在初始檔案系統中查詢init檔案,並基於該檔案啟動第一個使用者程式。
init進程裝載一個已經持久化的檔案系統,繼續初始化作業系統,從Linux檔案系統的根目錄轉移到已裝載的檔案系統,並啟動初始化所需的其他程式。
發行版是一個Linux核心以及安裝在計算機或裝置上的一組庫、實用元件和程式。
目前,各種發行版在市場上流行。我們可以從DistroWatch[1]上檢視這些清單。
現代的Linux發行版通常以ISO映象的形式釋出,並允許我們安裝更新和額外的程式(包),但我們現在要做的是一個極簡的發行版,因此對我們沒什麼意義。
關於如何從頭開始構建Linux發行版的最完整的說明,可以參考這裡[2]。構建Linux發行版是一個有趣的過程,可以讓我們學習到很多新的東西,但它非常耗時,這意味著我們需要很大的意志力才能從頭到尾完成。
我試圖將建立分發套件的簡化過程做到極致:我們不會掛載一個永久的檔案系統,但作為一個init檔案,我們將使用一個指令碼檔案,該指令碼檔案將執行最小初始化並啟動sh (shell)。
多年來,Linux已經可以移植到許多硬體平臺上。然而,每個平臺的Linux引導都是不同的。對於x86可能有如下不同:
它會被用來引導BIOS或者UEFI嗎?
BIOS或UEFI將在哪個儲存裝置上尋找載入程式(硬碟,快閃記憶體驅動器隨身碟,光碟機,網路)?
如何標記硬碟或隨身碟(MBR或GPT)?
核心文件和帶有初始根檔案系統映象的檔案(稱為initrd)位於什麼媒介上,在哪種檔案系統(FAT、NTFS、EXT、CDFS,等等)中?
初始的根檔案系統包含Linux後續操作所需的最小量級的目錄和檔案。在我們的例子中,這些是bin、dev、proc和sys目錄。bin目錄包含了使用Linux核心的實用工具。
極簡版的Linux是一個核心和一組命令列實用程式包。核心和命令列實用程式由不同的程式設計師團隊開發。
最常見的集合是:
GNU Core Utils;
BusyBox。
BusyBox結構簡單,佔用磁碟空間少,常用於嵌入式裝置。為了簡單起見,我們將使用這個。
這聽起來很矛盾,Linux通常被編譯成Linux。要做到這一點,我們需要一個Linux作業系統,其中包含允許我們構建Linux核心的程式和一組用於構建的實用程式包。
例如,在Ubuntu 22.10上,我們需要安裝以下軟體包:make、build-essential、bc、bison、flex、libssl-dev、libelf-dev、wget、cpio、fdisk、extlinux、dosfstool和qemu-system-x86。
注意,對於其他版本的Linux系統,包的集合可能不同。
安裝所需的程式包。
$ cd ~
$ mkdir -p simple-linux/build/sources
$ mkdir -p simple-linux/build/downloads
$ mkdir -p simple-linux/build/out
$ mkdir -p simple-linux/linux
$ sudo apt update
$ sudo apt install --yes make build-essential bc bison flex libssl-dev libelf-dev wget cpio fdisk extlinux dosfstools qemu-system-x86
從Linux Kernel上下載原始碼,再從Internet上下載BusyBox。
$ cd simple-linux/build
$ wget -P downloads
$ wget -P downloads
$ tar -xvf downloads/linux-5.15.79.tar.xz -C sources
$ tar -xjvf downloads/busybox-1.35.0.tar.bz2 -C sources
$ cd sources/busybox-1.35.0
$ make defconfig
$ make LDFLAGS=-static
$ cp busybox ../../out/
$ cd ../linux-5.15.79
$ make defconfig
$ make -j8 || exit
$ cp arch/x86_64/boot/bzImage ~/simple-linux/linux/vmlinuz-5.15.79
$ mkdir -p ~/simple-linux/build/initrd
$ cd ~/simple-linux/build/initrd
$ vi init
init檔案:
#! /bin/sh
mount -t sysfs sysfs /sys
mount -t proc proc /proc
mount -t devtmpfs udev /dev
sysctl -w kernel.printk="2 4 1 7"
/bin/sh
poweroff -f
建立目錄和檔案結構。
$ chmod 777 init
$ mkdir -p bin dev proc sys
$ cd bin
$ cp ~/simple-linux/build/out/busybox ./
$ for prog in $(./busybox --list); do ln -s /bin/busybox $prog; done
$ cd ..
$ find . | cpio -o -H newc > ~/simple-linux/linux/initrd-busybox-1.35.0.img
$ cd ~/simple-linux/linux
$ qemu-system-x86_64 -kernel vmlinuz-5.15.79 -initrd initrd-busybox-1.35.0.img -nographic -append 'console=ttyS0'
如果我們想在真正的硬體上執行Linux,那麼可能最簡單的方法是建立一個映象,放在引導驅動器上並啟動它。在我看來,建立這樣的映象並從驅動器啟動是最困難的過程,需要我們具備更高階的知識。
建立映象時,需要做以下幾個決定:
哪個會啟動引導(BIOS或UEFI)?
將使用哪個驅動器(光碟驅動器、隨身碟還是硬碟驅動器)?
如何對驅動器(MBR、GPT)進行分割槽?
使用哪個載入程式?
將使用什麼檔案系統,Linux和引導載入程式檔案將放在哪裡?
我用了一個安裝了MBR和EXTLINUX引導裝載程式的隨身碟,檔案放在了一個FAT32分割槽中。BIOS為我啟動了引導過程(如果你的計算機有UEFI BIOS,則為選擇Legacy boot選項)。
建立可引導隨身碟映象的方法如下:
建立一個映象檔案。
$ dd if=/dev/zero of=boot-disk.img bs=1024K count=50
$ echo "type=83,bootable" | sfdisk boot-disk.img
$ losetup -D
$ LOOP_DEVICE=$(losetup -f)
$ losetup -o $(expr 512 \* 2048) ${LOOP_DEVICE} boot-disk.img
$ mkfs.vfat ${LOOP_DEVICE}
$ mkdir -p /mnt/os
$ mount -t auto ${LOOP_DEVICE} /mnt/os
$ cp vmlinuz-5.15.79 initrd-busybox-1.35.0.img /mnt/os
$ mkdir -p /mnt/os/boot
$ extlinux --install /mnt/os/boot
$ echo "DEFAULT linux" >> /mnt/os/boot/syslinux.cfg
$ echo " SAY Booting Simple Linux via SYSLINUX" >> /mnt/os/boot/syslinux.cfg
$ echo " LABEL linux" >> /mnt/os/boot/syslinux.cfg
$ echo " KERNEL /vmlinuz-5.15.79" >> /mnt/os/boot/syslinux.cfg
$ echo " APPEND initrd=/initrd-busybox-1.35.0.img nomodeset" >> /mnt/os/boot/syslinux.cfg
$ umount /mnt/os
$ losetup -D
$ dd if=/usr/lib/syslinux/mbr/mbr.bin of=boot-disk.img bs=440 count=1 conv=notrunc
boot-disk.img檔案將會包含隨身碟的引導映象。
上述方法包含許多命令和引數;敲這麼多程式碼很容易出錯。我們可以將命令組合成bash指令碼,為了能夠在Windows 10或11作業系統上構建Linux,建議使用Docker Desktop。
Docker的本質如下:
在Dockerfile中,描述程式或指令碼的基礎環境結構。
使用Docker實用工具,基於Dockerfile,以特定格式建立該環境的映象。
使用相同的工具,可以啟動基於映象的程式或指令碼例項,這些例項執行在隔離的環境中,在Docker術語中稱為Docker容器。
建立好的映象可以儲存在存映象倉庫中並進行復用。由同一個映象建立的Docker容器是可以在其他計算機上同樣執行。
Dockerfile易於閱讀和學習,也易於釋出。
下面是Dockerfile的內容:
FROM ubuntu:22.10
RUN apt update && apt install --yes make build-essential bc bison flex libssl-dev libelf-dev wget
RUN apt install --yes cpio fdisk extlinux dosfstools qemu-system-x86
RUN apt install --yes vim
ARG APP=/app
ARG LINUX_DIR=$APP/linux
ARG FILES_DIR=$APP/files
ARG SCRIPTS_DIR=$APP/scripts
ENV BUILD_DIR=$APP/build
ENV LINUX_DIR=$LINUX_DIR
ENV FILES_DIR=$FILES_DIR
ENV LINUX_VER=5.15.79
ENV BUSYBOX_VER=1.35.0
ENV BASH_ENV="$SCRIPTS_DIR/bash-env/env"
COPY ./scripts $APP/scripts
COPY ./files $APP/files
RUN mkdir -p $LINUX_DIR
RUN ln -s $APP/scripts/start-linux.sh /usr/bin/start &&\
ln -s $APP/scripts/build-linux.sh /usr/bin/build &&\
ln -s $APP/scripts/build-image.sh /usr/bin/image
WORKDIR $APP/scripts
CMD build
The FROM command是最重要的,它指定了我們的Linux構建映象將基於哪個系統映象。在本例,我們是Ubuntu 22.10。
The RUN command在建立的映象中執行命令。也就是說,RUN之後的命令將像執行Ubuntu 22. 10一樣在命令列中執行。執行該命令後,系統映象將發生變化,因為這些命令更改了其中的檔案系統。
The COPY command將檔案從作業系統的檔案系統複製到建立的映象中。與RUN類似,它也會修改映象中的檔案系統。
The ARG and ENV commands比較令人困惑,不知道我是否會說清楚,ARG是在建立映象時使用的變數,而ENV是在基於該映象建立容器時使用的變數,同時此變數在容器中是可見的。
The WORKDIR command指定啟動基於我們的映象建立的容器時,在哪個目錄下工作。
The CMD command命令指定了容器啟動時預設執行的命令。
我們可以試驗一下這個專案。在Windows上,最好在PowerShell中執行Docker。
建立一個Docker映象。
$ git clone
$ cd simple-linux
$ docker build --build-arg APP -t simple-linux .
$ mkdir linux
$ cd linux
$ docker run -v ${pwd}:/app/linux --rm -it simple-linux build
為隨身碟建立一個引導映象。
注意,在Docker中需要使用--privileged選項,因為映象指令碼使用迴環裝置。
$ docker run -v ${pwd}:/app/linux –-privileged --rm -it simple-linux image
為隨身碟(linux-5.15.79-busybox-1.35.0-disk.img)建立的映象檔案可以使用Win32DiskImager寫入隨身碟。
需要注意的是,在寫入時,隨身碟上現有的資料會丟失!
在將映象寫入隨身碟後,重新啟動計算機並選擇從USB-HDD啟動,即可以從剛建立的隨身碟啟動。如果遇到問題,最可能的情況是,在此之前,需要在BIOS中選擇“Legacy Boot”並禁用“Secure Boot”。
安裝Docker Desktop for Windows後,並在PowerShell中使用一個簡單的命令來構建一個極簡的Linux作業系統。
docker run -v ${pwd}:/app/linux --rm -it artyomsoft/simple-linux build
在這篇文章中,我儘量提供詳細的說明,告訴大家如何從原始碼獲得一個可以工作的Linux系統。
對一些人來說,這篇文章可能看起來很簡單,不值得關注。但是,為了不被細節嚇到,我沒有深入討論諸如BIOS、UEFI、檔案系統、bootloader、glibc庫、詳細的作業系統引導過程、各種規範、動態和靜態連結、Linux核心模組……我只是給出了讓大家能夠接受和理解的最基本的理論,事實上,這能讓大家更快速地理解這個主題,而不是像我之前那也一點一點地收集所有資訊。
我們其實幾乎不會在未來使用這樣的作業系統,這篇文章也只是希望大家能將Linux的抽象知識轉化為對Linux更深的理解和一些小技能。
這篇文章有用嗎?你是否嘗試了從原始碼構建一個極簡的Linux作業系統?歡迎大家在評論中分享你的經驗!
相關連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024420/viewspace-2936625/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 自制 os 極簡教程1:寫一個作業系統有多難作業系統
- 透過chroot構建Linux作業系統(轉)Linux作業系統
- truncate一個linux作業系統檔案Linux作業系統
- Linux作業系統原始碼詳細分析(二)(轉)Linux作業系統原始碼
- Linux作業系統原始碼詳細分析(三)(轉)Linux作業系統原始碼
- 如何構建一個系統?
- 自制作業系統(一) 第一個作業系統作業系統
- 作業系統(二):作業系統結構作業系統
- AvaloniaChat—從原始碼構建指南原始碼
- 從零構建一個簡單的 Python 框架Python框架
- Linux作業系統——簡單程式同步Linux作業系統
- Linux作業系統記憶體管理的原始碼實現(轉)Linux作業系統記憶體原始碼
- 用Akka構建一個簡易的分散式檔案系統分散式
- 各個作業系統的 作業系統日誌作業系統
- JavaWeb——從零開始構建一個客戶管理系統(一)JavaWeb
- 作業系統(一):作業系統概述作業系統
- 跟大神一起製作一個屬於自己的Linux作業系統!Linux作業系統
- 作業系統: Unix作業系統演進簡史作業系統
- 從原始碼構建docker-ce原始碼Docker
- 作業系統簡介作業系統
- 圖資料庫|如何從零到一構建一個企業股權圖譜系統?資料庫
- 圖資料庫|如何從零到一構建一個企業股權圖譜系統資料庫
- 【作業系統】作業系統綜述(一)作業系統
- 趣談Linux作業系統-劉超-極客時間Linux作業系統
- 一個使用 Composer 構建的極簡 Laravel 核心功能框架 larasimpleLaravel框架
- 作業系統-第一章-系統簡介作業系統
- 程式碼來構建一個簡單的compilerCompile
- 作業系統結構作業系統
- 有人想學習作業系統原始碼嗎作業系統原始碼
- Linux 作業系統Linux作業系統
- Linux作業系統Linux作業系統
- Linux 下檢視開源軟體或作業系統的原始碼(轉)Linux作業系統原始碼
- Wii將有一個可升級的Linux作業系統(轉)Linux作業系統
- 【linux】Linux作業系統Linux作業系統
- 直播系統原始碼,極光IM簡單的聊天介面全手動原始碼
- 1.3作業系統簡介作業系統
- 作業系統核心簡介作業系統
- 作業系統體系結構作業系統