本項目給大家演示了怎麼樣快速製作一個小巧且功能齊全的linux 操作系統, 項目地址https://github.com/superconvert/smart-os

我們為什麼選擇server 版本進行製作?
server 版本不包含窗口系統所依賴的大部分包;如果系統自帶這些包,就會存在包的多版本的問題,編譯問題,依賴問題,鏈接問題,運行時問題,會給我們的給工作帶來很多干擾,況且解決這些問題都是無意義的,我們需要純淨版本的依賴包
為什麼窗口系統工作如此龐大?
我們所有利用apt 安裝的包(工具除外),理論上都需要我們進行源碼編譯,包括包的依賴及粘連,都需要解決,這是一個極其龐大的工作量,沒辦法,新系統上一無所有,所需的環境都需要我們交叉編譯提供出來。工程A 依賴包b ,包b 依賴包c, 包c 又依賴包d,我們所要做的就是把所有的包都需要編譯出來!
本腳本Ubuntu 18.04 上做的,別的系統應該改動不大,有需要的朋友可以自行修改。
準備系統環境由於內核需要編譯,需要安裝內核編譯所需要的環境由於busybox 需要編譯,根據需要自行安裝所需環境
./00_build_env.sh編譯源碼( kernel, glibc, busyboxy, gcc, binutils)
./01_build_src.sh製作系統盤( 重要,此步驟把系統安裝到一個系統文件內)
./02_build_img.sh運行smart-os 系統
./03_run_qemu.sh 或 ./04_run_docker.sh是不是製作一個操作系統很簡單! 磁盤空間可以任意擴展,可以上網,可以根據需要擴展自己想要的組件,我已經試驗成功,在smart-os 內運行流媒體服務器smart_rtmpd 了
+----------------------------------------------------------------+-----------------------------------------+-----------------------------------------+
| Host | Container 1 | Container 2 |
| | | |
| +------------------------------------------------+ | +-------------------------+ | +-------------------------+ |
| | Newwork Protocol Stack | | | Newwork Protocol Stack | | | Newwork Protocol Stack | |
| +------------------------------------------------+ | +-------------------------+ | +-------------------------+ |
| + + | + | + |
| ............... | .................... | ........................... | ................... | ..................... | .................... | .................... |
| + + | + | + |
| +-------------+ +---------------+ | +---------------+ | +---------------+ |
| | 192.168.0.3 | | 192.168.100.1 | | | 192.168.100.6 | | | 192.168.100.8 | |
| +-------------+ +---------------+ +-------+ | +---------------+ | +---------------+ |
| | eth0 | | br0 | < --- > | tap0 | | | eth0 | | | eth0 | |
| +-------------+ +---------------+ +-------+ | +---------------+ | +---------------+ |
| + + + | + | + |
| | | | | | | | |
| | + +------------------------------+ | | |
| | +-------+ | | | |
| | | tap1 | | | | |
| | +-------+ | | | |
| | + | | | |
| | | | | | |
| | +-------------------------------------------------------------------------------------------+ |
| | | | |
| | | | |
+--------------- | ------------------------------------------------+-----------------------------------------+-----------------------------------------+
+
Physical Network (192.168.0.0/24)由於smart-os安裝了glibc 動態庫,這個嚴重依賴動態庫加載器/鏈接器ld-linux-x86-64.so.2 ,由於應用程序都是通過動態編譯鏈接的,當一個需要動態鏈接的應用被操作系統加載時,系統必須要定位然後加載它所需要的所有動態庫文件。這項工作是由ld-linux.so.2 來負責完成的,當程序加載時,操作系統會將控制權交給ld-linux.so 而不是交給程序正常的入口地址。 ld-linux.so.2 會尋找然後加載所有需要的庫文件,然後再將控制權交給應用的起始入口。 ld-linux-x86-64.so.2 其實就是ld-linux.so.2 的軟鏈,它必須存在於/lib64/ld-linux-x86-64.so.2 下,否則,我們動態編譯的busybox 依賴glibc 庫,glibc 庫的加載需要ld-linux-x86-64.so,如果/lib64目錄下不存在它,就會導致系統啟動時會直接panic,這個問題需要特別注意! ! !
qemu 一般啟動後窗口比較小,一旦出現錯誤,基本上沒辦法看錯誤日誌,那麼就需要在grub 的啟動項內增加console=ttyS0,同時qemu-system-x86_64 增加串口輸出到文件-serial file:./qemu.log,這樣調試就方便多了,調試完畢需要去掉console=ttyS0 否則,/etc/init.d/rcS 裡面的內容可能輸出不顯示
我們編譯的glibc ,通常情況下版本都會高於系統自帶的版本,我們可以編寫測試程序main.c 用來測試glibc 是否編譯成功。比如:
# include < stdio.h >
int main () {
printf ( " Hello glibc n " );
return 0 ;
}我們進行編譯
gcc -o test main.c -Wl,-rpath=/root/smart-os/work/glibc_install/usr/lib64 編譯成功,我們執行./test 程序,通常會報類似於這樣的錯誤/lib64/libc.so.6: version `GLIBC_2.28' not found 或者程序直接segment 了其實這是沒有指定動態庫加載器/鏈接器和系統環境的原因,通常我們編譯glibc 庫的時候,編譯目錄會自動生成一個testrun.sh 腳本文件,我們在編譯目錄內執行程序
./testrun.sh ./test 通常這樣就可以成功執行。我們也可以把下面一句話保存到一個腳本中,執行測試也是可以的。
exec env /root/smart-os/work/glibc_install/lib64/ld-linux-x86-64.so.2 --library-path /root/smart-os/work/glibc_install/lib64 ./test我們怎麼跟踪一個可執行程序的加載那些庫,利用LD_DEBUG=libs ./test 就可以了, 我們預加載庫可以利用LD_PRELOAD 強制預加載庫
我們編譯cairo 通常情況下會遇到很多問題,如果cairo 編譯出現問題,怎麼辦,有些錯誤信息網上很難搜到一定看它編譯時生成的config.log 文件,錯誤信息很詳細!可以根據提示信息去解決問題
關於busybox 的init 系統變量即使利用grub 的內核參數,傳遞環境變量也不行,busybox 的init 會自動生成默認的環境變量PATH ,因此需要改動源碼支持自定制路徑。當然shell 的登錄模式,會讀取/etc/profile ,對於非登錄模式,此模式失效,所以通過/etc/profile 有局限性。
這塊知識牽涉的知識相對比較龐大,國內包括國外專門介紹xfce4 的編譯及使用文章相對較少,我也是摸著石頭過河,盡量把這塊知識演示清楚,我會專門開一個章節專門講解這個,對於xfce4 移植到smart-os 內,給國人揭示圖形系統的奧秘,具體詳情請參見xfce4.md
整個圖形系統的整合工作量特別龐大,牽涉到系統的方方面面,國外相關此方面系統性的資料都比較少,國內幾乎就更少了。目標是全部自己DIY 所有的環境,個人的開源項目,讓整個圖形系統完整的運行起來,smart-os 不是第一個,基本上也是前三名。目前我還不知道。整個整合過程非常漫長,遇到的問題非常非常多,不斷的調試,編譯,這些重複性的工作就不說了,工作量特別龐大,我幾乎可以用嘔心瀝血來形容我的工作,絕對不為過。其次,遇到的知識點也比較多,很多都是現學現賣,需要迅速了解其工作機制,出問題的原因,然後解決問題。下面就整體的思路大體說一下,方便新學者,快速了解思路,對系統維護有個指引,對於解決系統性問題提供一個模型。
usr 目錄詳解usr = unix system resource 的縮寫, /lib 庫是內核級別的庫,/usr/lib 是系統級別的庫,/usr/local/lib 是應用級別的庫;/lib 包含許多/bin && /sbin 中可執行程序使用的庫。 /usr/lib 幾乎所有的系統可執行程序引用的庫都會安裝在這裡,/usr/local/bin 很多應用層可執行性程序引用的庫都放到這裡
ramfs :
ramfs是一種非常簡單的文件系統,它直接利用linux內核已有的高速緩存機制(所以其實現代碼很小, 也由於這個原因, ramfs特性不能通過內核配置參數屏蔽, 它是內核的天然屬性), 使用系統的物理內存,做成一個大小可以動態變化的的基於內存的文件系統, 系統不會回收, 只有root 用戶使用它
tmpfs :
tmpfs是ramfs的衍生物,在ramfs的基礎上增加了容量大小的限制和允許向交換空間(swap) 寫入數據。由於增加了這兩個特性,所以普通用戶也可以使用tmpfs。 tmpfs 佔用的是虛擬內存,不全是RAM ,性能可能沒ramfs 高
ramdisk :
ramdisk是一種將內存中的的一塊區域作為物理磁盤來使用的一種技術,也可以說,ramdisk是在一塊內存區域中創建的塊設備,用於存放文件系統。對於用戶來說,可以把ramdisk與通常的硬盤分區同等對待來使用。系統讀寫它還會在內存中存儲一份對應的緩存,污染CPU 緩存,性能也差,需要對應驅動支持
rootfs :
rootfs是一個特定的ramfs(或tmpfs,如果tmpfs被啟用)的實例,它始終存在於linux2.6的系統中。 rootfs不能被卸載(與其添加特殊代碼用來維護空的鍊錶, 不如把rootfs節點始終加入,因此便於kernel維護。rootfs是ramfs的一個空實例,佔用空間極小)。大部分其他的文件系統安裝於rootfs之上,然後忽略它。 它是內核啟動初始化根文件系統。
rootfs又分為虛擬rootfs和真實rootfs。
虛擬rootfs由內核自己創建和加載,僅僅存在於內存之中(後續的InitRamfs也是在這種基礎上實現),其文件系統是tmpfs類型或者ramfs類型。
真實rootfs則是指根文件系統存在於存儲設備上,內核在啟動過程中會在虛擬rootfs上掛載這個存儲設備,然後將/目錄節點切換到這個存儲設備上,這樣存儲設備上的文件系統就會被作為根文件系統使用(後續InitRamdisk是在這種基礎上實現),其文件系統類型更加豐富,可以是ext2、yaffs、yaffs2等等類型, 由具體的存儲設備的類型決定。
我們的啟動文件系統其實就是為rootfs 準備文件,讓內核按照我們的意願執行在早期的linux系統中,一般只有硬盤或者軟盤被用來作為linux根文件系統的存儲設備,因此也就很容易把這些設備的驅動程序集成到內核中。但是現在的嵌入式系統中可能將根文件系統保存到各種存儲設備上,包括scsi、sata,u-disk等等。因此把這些設備的驅動代碼全部編譯到內核中顯然就不是很方便。 在內核模塊自動加載機制udev中,我們看到利用udevd可以實現內核模塊的自動加載,因此我們希望如果存儲根文件系統的存儲設備的驅動程序也能夠實現自動加載, 那就好了。但是這裡有一個矛盾,udevd是一個可執行文件,在根文件系統被掛載前,是不可能執行udevd的,但是如果udevd沒有啟動,那就無法自動加載存儲根文件系統設備的驅動程序,同時也無法在/dev目錄下建立相應的設備節點。 為了解決這一矛盾,於是出現了基於ramdisk的initrd( bootloader initialized RAM disk )。 Initrd是一個被壓縮過的小型根目錄,這個目錄中包含了啟動階段中必須的驅動模塊,可執行文件和啟動腳本,也包括上面提到的udevd(實現udev機制的demon)。當系統啟動的時候,bootloader會把initrd文件讀到內存中,然後把initrd文件在內存中的起始地址和大小傳遞給內核。內核在啟動初始化過程中會解壓縮initrd文件,然後將解壓後的initrd掛載為根目錄,然後執行根目錄中的/init 腳本(cpio格式的initrd為/init,而image格式的initrd<也稱老式塊設備的initrd或傳統的文件鏡像格式的initrd>為/initrc),您就可以在這個腳本中運行initrd 文件系統中的udevd,讓它來自動加載realfs(真實文件系統)存放設備的驅動程序以及在/dev目錄下建立必要的設備節點。在udevd自動加載磁盤驅動程序之後,就可以mount真正的根目錄,並切換到這個根目錄中來。