打造屬於自己的linux發行版

linux的啟動過程,包括BIOS的加電自檢POST,拷貝MBR的信息(啟動BootLoader),加載內核,掛載根文件安系統這幾大步熟悉grub的話會知道linux啟動時grub中有三項:root,kernel,initrd。其三項的作用分別是:

1.指定內核所在的目錄
2.指定內核的名稱,以及掛載根目錄的方式,還有向內核傳遞一定的參數
3.initrd實際就是個小的linux系統,在一般的系統中initrd的作用是:啟動一個很小的linux用來掛載真實的linux。

今天的目的就是從內核開始,打造一個屬於自己的linux。

環境:Ubuntu13.04 gcc4.7.3
相關的準備工作: 內核的編譯
qemu的安裝

##製作根系統目錄

1)創建init程序

首先創建一個init.c文件,代碼如下:

#include<stdio.h>
int main()
{
    printf("Welcome! My student No. is sa*****310.\n");
    return 0;
}

靜態編譯成一個可執行文件。

gcc -static -o init init.c

2)建立引導根目錄映像

終端運行:

dd if=/dev/zero of=initrd.img bs=4096 count=1024
mkfs.ext3 initrd.img

有提示,輸入y。

###關於dd dd 是 Linux/UNIX 下的一個非常有用的命令,作用是用指定大小的塊拷貝一個文件,並在拷貝的同時進行指定的轉換。

語法:dd [選項] 
if =輸入文件(或設備名稱)。 
of =輸出文件(或設備名稱)。 
ibs = bytes 一次讀取bytes字節,即讀入緩衝區的字節數。 
...

3)創建rootfs目錄,並掛載

mkdir rootfs
sudo mount -o loop initrd.img rootfs

4)在rootfs中添加一些文件 將init拷貝到initrd4M.img的目標根目錄下(因為linux啟動後期會在根目錄中尋找一個應用程序來運行,在根目錄下提供init是一種可選方案)

cp init rootfs/

準備dev目錄:

sudo mkdir rootfs/dev

linux啟動過程中會啟用console設備: sudo mknod rootfs/dev/console c 5 1 另外需要提供一個linux根設備,我們使用ram:

sudo mknod rootfs/dev/ram b 1 0
sudo umount rootfs

至此,一個包含簡單應用程序的根目錄initrd4M.img映像就準備好。

mknod 用於製作字符或塊相關文件

用qemu跑一下:

qemu -kernel ../linux-3.9.2/arch/x86/boot/bzImage -initrd initrd.img -append "root=/dev/ram init=/init"

整合busybox

busybox簡介 BusyBox 是一個集成了一百多個最常用linux命令和工具的軟件。BusyBox 包含了一些簡單的工具,例如ls、cat和echo等等,還包含了一些更大、更復雜的工具,例如grep、find、mount以及telnet。有些人將 BusyBox 稱為 Linux 工具裡的瑞士軍刀。簡單的說BusyBox就好像是個大工具箱,它集成壓縮了 Linux 的許多工具和命令,也包含了 Android 系統的自帶的shell。 BusyBox 將許多具有共性的小版本的UNIX工具結合到一個單一的可執行文件。這樣的集合可以替代大部分常用工具比如的GNU fileutils , shellutils等工具,BusyBox提供了一個比較完善的環境,可以適用於任何小的或嵌入式系統。

下載源碼:http://www.busybox.net/ 這裡選擇1.20穩定版。 解壓,終端進入目錄執行:

make menuconfig

勾選下面的選項:

Build Options
Build BusyBox as a static binary (no shared libs)

這個選項是一定要選擇的,這樣才能把busybox編譯成靜態鏈接的可執行文件,運行時才獨立於其他函數庫.否則必需要其他庫文件才能運行,在單一個linux內核不能使他正常工作.

現在直接make的話會報錯:‘RLIMIT_FSIZE’ undeclared 論壇上的回答是沒有包含 sys/resource.h,則在include/libbb.h 中添加:

#include <sys/resource.h>  

接下來執行:

#編譯busybox
make 
#安裝busybox
make install 

安裝好之後在文件夾下出現一個_install文件夾,編譯完成。

下面來整合根文件系統。 新建一個文件夾,終端cd進去,將之前的initrd.img拷貝進來。

#創建文件夾
mkdir rootfs
#掛載鏡像
sudo mount -o loop initrd.img rootfs/ 
#將busybox添加進來
cd ../busybox-1.20.2/
sudo make CONFIG_PREFIX=../Opuntu/rootfs/ install
#查看rootfs中結構
cd ../Opuntu
ls rootfs
 #卸載分區
sudo umount rootfs/

運行命令時注意目錄結構!
linux的系統下的目錄都是幹嘛的?
linux下的文件結構,看看每個文件夾都是幹嗎用的

/bin 二進制可執行命令 
/dev 設備特殊文件 
/etc 系統管理和配置文件 
/etc/rc.d 啟動的配置文件和腳本 
/home 用戶主目錄的基點,比如用戶user的主目錄就是/home/user,可以用~user表示 
/lib 標準程序設計庫,又叫動態鏈接共享庫,作用類似windows裡的.dll文件 
/sbin 系統管理命令,這裡存放的是系統管理員使用的管理程序 
/tmp 公用的臨時文件存儲點 
/root 系統管理員的主目錄(呵呵,特權階級) 
/mnt 系統提供這個目錄是讓用戶臨時掛載其他的文件系統。 
...

##qemu測試

qemu -kernel ../linux-3.9.2/arch/x86/boot/bzImage -initrd initrd.img -append "root=/dev/ram init=/bin/sh"

裝載好之後可以在qemu中運行busybox的命令,效果如下:

##整合grub 關於Grub GNU GRUB(簡稱“GRUB”)是一個來自GNU項目的多操作系統啟動程序。GRUB是多啟動規範的實現,它允許用戶可以在計算機內同時擁有多個操作系統,並在計算機啟動時選擇希望運行的操作系統。GRUB可用於選擇操作系統分區上的不同內核,也可用於向這些內核傳遞啟動參數。 首先來測試一下grub. 從ftp://alpha.gnu.org/gnu/grub/下載GRUB Legacy的最後一個版本0.97的編譯好的文件grub-0.97-i386-pc.tar.gz. 解壓之後中端cd進去,執行下面的命令。

#建立軟盤映像:
dd if=/dev/zero of=boot.img bs=512 count=2880
#在boot.img中安裝grub:
sudo losetup /dev/loop0 boot.img
sudo dd if=./grub-0.97-i386-pc/boot/grub/stage1 of=/dev/loop0 bs=512 count=1
sudo dd if=./grub-0.97-i386-pc/boot/grub/stage2 of=/dev/loop0 bs=512 seek=1
sudo losetup -d /dev/loop0

在qemu中測試是否可以進入grub

qemu -fda boot.img

OK. 接下來將grub,kernel,busybox一起整合,離勝利只還有一步 - - 。 先你自己發行版取一個庫一些的名字,比如Opuntu...(Opensuse+ubuntu),創建一個以它命名的文件夾。

#拷貝boot.img到當前目錄
sudo cp ../grub-0.97-i386-pc/boot.img ./
#創建rootfs文件夾
mkdir rootfs
#創建一個32M的磁盤鏡像文件
dd if=/dev/zero of=/dev/zero of=Opuntu.img bs=4096 count=8192
#給磁盤映像分區
fdisk -C 16065 -H 255 -S 63 Opuntu.img

解釋:設置Opuntu.img的磁頭數為255、磁道數為16065、扇區數為63,同時給磁盤分區。 這裡我們只分一個區,並設置該分區為引導分區。示意圖如下:

#格式化分區
sudo losetup -o 1048576 /dev/loop0 Opuntu.img
sudo mkfs.ext3 -m 0 /dev/loop0

解釋:我們把前面的2048個扇區(0~2047)作為引導扇區使用,格式化分區從第2048個扇區開始,所以1048576=2048*512

#拷貝之前做好的initrd.img和bzImage.img到rootfs
sudo mount /dev/loop0 rootfs/
sudo cp ../linux-3.9.2/arch/x86/boot/bzImage ./rootfs/
sudo cp ../rootfs/initrd.img ./rootfs/
#添加grub
sudo mkdir rootfs/boot
sudo mkdir rootfs/boot/grub
sudo cp ../grub-0.97-i386-pc/boot/grub/* ./rootfs/boot/grub
sudo vi ./rootfs/boot/grub/menu.lst

內容:

default 0
timeout 30
title linux on 32M.img
root (hd0,0)
kernel (hd0,0)/bzImage root=/dev/ram init=/bin/ash
initrd (hd0,0)/initrd.img
#卸載磁盤鏡像
sudo umount rootfs
sudo losetup -d /dev/loop0
#利用grub啟動軟盤,在硬盤映像上添加grub功能
qemu -boot a -fda boot.img -hda Opuntu.img

執行圖中的兩步(注意空格):

運行成功之後,Opuntu.img就是我們的最終成果了,集成了busybox,grub,linux kernel3.92!

qemu -hda Opuntu.img

用qemu跑起來:

##參考: 鳥哥私房菜 第二十二章:開關機流程與loader 製作可用grub引導Linux系統的磁盤映像文件 - http://blog.sina.com.cn/s/blog_70dd169101013gcw.html

詳細講解Linux啟動流程及啟動用到的配置文件及腳本 - http://guodayong.blog.51cto.com/263451/1168731

更新內核

直接安裝的系統內核版本一般不是最新,用

uname -a  

命令可以查看內核的版本號,比如我的就是:

下面來手動更新內核到最新的穩定版本。

1.獲取源碼

最新的穩定版本是3.9.4. 下載好之後解壓到 /usr/src 文件夾下

sudo tar -xvf linux-3.9.4.tar.xz -C /usr/src/  

2.配置內核 將原來的配置文件拷過來

cp /usr/src/linux-headers-2.6.32-27-generic/.config .config  

首先進行一下配置,進入到 /usr/src//linux-3.9.4 文件夾下,執行

make menuconfig  

報錯:

*** Unable to find the ncurses libraries or the
*** required header files.

缺少ncurses庫(一個管理應用程序在字符終端顯示的函數庫),怒裝之:

sudo apt-get install ncurses-dev  

重新執行make menuconfig
選Load,然後Ok,然後Save。

3.編譯和安裝 終端執行:

make bzImage #編譯kernel  
make modules #編譯模塊  
make modules_install  #先安裝模塊
make install #安裝內核

編譯的時間由機器性能決定。

make install之後,grub已經自動更新,不用再手動設置引導。 重啟,進入ubuntu,更新後的第一次加載會有些慢。 再次查看內核版本,終端運行

uname -a  

###添加系統調用

系統調用的原理如下:

源碼目錄下涉及內核的三個文件有:

/kernel/sys.c                                         //定義系統調用
/arch/x86/syscalls/syscall_32.tbl      //設置系統調用號
/include/linux/syscalls.h                     //系統調用的頭文件

下面來實現一個簡單的系統調用。

1)系統調用函數的實現。 在/kernel/sys.c的最後添加下面的代碼:

asmlinkage int sys_callquansilang(int num)
{
	printk("Hi,I'm Quansilang. My student No. is sa*****310!");
	return 1;
}

2)設置系統調用號

編輯arch/x86/syscalls/syscall_32.tbl 文件,發現已經由350個定義號的系統調用,照葫蘆畫葫蘆娃,在最後添加自己的系統調用:

351 i386    callquansilang      sys_callquansilang  

注意要與之前定義的函數對應。

3)添加系統調用的聲明到頭文件

打開 /include/linux/syscalls.h ,倒數第二行添加

asmlinkage int sys_callquansilang(int num); 

4)重新編譯內核並安裝。

sudo make mrproper  #清除久的編譯文件
sudo make             #編譯

安裝:

make modules_install  #先安裝模塊
make install #安裝內核

重啟系統。

5)測試

創建一個main.c

#include <unistd.h>
#include <stdio.h>
int main()
{
    syscall(351, 1);
    return 1;

}

編譯運行,然後用

sudo dmesg -c

查看系統調用log(寫系統log會有一點延遲)。

##參考 向linux內核添加系統調用新老內核比較 - http://www.cnblogs.com/albert1017/archive/2013/05/27/3101760.html ubuntu 12.10 x64 下編譯新內核 + 系統調用方法 - http://blog.csdn.net/dslovemz/article/details/8744352 Linux添加內核系統調用報告 - http://blog.csdn.net/yming0221/article/details/6559767


书籍推荐