linux的啟動配置文件(grub):
GRUB:系統引導管理器。GRUB啟動時會在 /boot/grub 中尋找一個名字為grub.conf的設置文件,如果找不到此設置文件則不進入菜單模式而直接進入命令行模式。
grub.conf是一個純文字文件,您可以用任何一個文字編輯器來打開它。每一行代表一個設置命令,如果一行的第一個字符為井號#,則這一行為注釋,您可以簡單地用增加或減少注釋行來改變設置。
用vi打開grub.conf,內容如下:
default=0
timeout=10
splashimage=(hd0,6)/grub/splash.xpm.gz
title Red Hat Linux (2.4.20-8)
root (hd0,0)
kernel /vmlinuz-2.4.20-8 ro root=LABEL=/
initrd /initrd-2.4.20-8.img
timeout second
設置在second秒之后引導默認的操作系統。
Linux 默認是timeout 10,也就是說10秒之內如果沒有其他命令就啟動系統。如果設成 -1,則 GRUB 會一直等待直到用戶選擇一個選項為止。
default num
默認啟動第 num +1行選項,也就說default=0,則默認啟動菜單第1行的操作系統,default=1,則啟動第2行的系統,如此類推。
splashimage指定了開機畫面文件splash.xpm.gz的位置.
title: 定義啟動菜單項的名稱
root: 設置 Grub 的根設備 (root)為 Linux 內核所在分區
hd0是指第一個硬盤(主硬盤),(hd0,0)是指第一個硬盤的第一個分區。
kernel: 后跟 Linux 內核文件為參數, 加載 Linux 內核文件
kernel /vmlinuz是指出Linux內核的路徑在/vmlinuz中。
initrd: 加載鏡像文件
如果自己重新編譯了內核,可把這個內核加載到grub,開機時可以選擇運行哪個內核。(把這段加在原有內容的后面,想啟動幾個就加幾個)
如:title Linux (2.4.18)
root (hd0,0)
kernel /bzImage ro root=/dev/hda2
initrd /initrd-2.4.20-8.img
改一下啟動菜單的名稱,把編譯好的內核bImage掛載到/dev/hda2
如果硬盤用的是scsi的,掛載到/dev/sda2,在make menuconfig時,以下選項必選:
Device Drivers --->SCSI device support ---><*> SCSI disk support
Device Drivers --->SCSI device support --->SCSI low-level drivers ---> <*> BusLogic SCSI support
Open()
頭文件: #include<fcntl.h>
定義函數 int open( const char * pathname, int flags);
int open( const char * pathname,int flags, mode_t mode);
Open()和 C 中的fopen相似,定義int fd;fd=open(“filename”,打開方式);
返回值:此函數調用成功后(所有欲核查的權限都通過了檢查)將返回所打開的文件的文件描述符(int類型),表示成功,如果調用失敗,或只要有一個權限被禁止則返回-1。
O_RDONLY 以只讀方式打開文件
O_WRONLY 以只寫方式打開文件
O_RDWR 以可讀寫方式打開文件。上述三種是互斥的,也就是不可同時使用,但可與下列的利用OR(|)運算符組合。
O_CREAT 若欲打開的文件不存在則自動建立該文件。
O_EXCL 如果O_CREAT 也被設置,此指令會去檢查文件是否存在。文件若不存在則建立該文件,否則將導致打開文件錯誤。此外,若O_CREAT與O_EXCL同時設置,並且欲打開的文件為符號連接,則會打開文件失敗。
O_NOCTTY 如果欲打開的文件為終端機設備時,則不會將該終端機當成進程控制終端機。
O_TRUNC 若文件存在並且以可寫的方式打開時,此旗標會令文件長度清為0,而原來存於該文件的資料也會消失。
O_APPEND 當讀寫文件時會從文件尾開始移動,也就是所寫入的數據會以附加的方式加入到文件后面。
O_NONBLOCK 以不可阻斷的方式打開文件,也就是無論有無數據讀取或等待,都會立即返回進程之中。
O_NDELAY 同O_NONBLOCK。
O_SYNC 以同步的方式打開文件。
O_NOFOLLOW 如果參數pathname 所指的文件為一符號連接,則會令打開文件失敗。
O_DIRECTORY 如果參數pathname 所指的文件並非為一目錄,則會令打開文件失敗。
Read()
頭文件: #include<unistd.h>
定義函數 ssize_t read(int fd,void * buf ,size_t count);
函數說明 read()會把參數fd 所指的文件傳送count個字節到buf指針所指的內存中。若參數count為0,則read()不會有作用並返回0。返回值為實際讀取到的字節數,如果返回0,表示已到達文件尾或是無可讀取的數據,此外文件讀寫位置會隨讀取到的字節移動。
附加說明 如果順利read()會返回實際讀到的字節數,最好能將返回值與參數count 作比較,若返回的字節數比要求讀取的字節數少,則有可能讀到了文件尾、從管道(pipe)或終端機讀取,或者是read()被信號中斷了讀取動作。當有錯誤發生時則返回-1,錯誤代碼存入errno中,而文件讀寫位置則無法預期。
文件描述符:
任何打開的文件都將被分配一個唯一標識該打開文件夾的文件描述符(也稱文件句柄),為一個大於等於0的整數。系統啟動后,默認打開的文件流有標准輸入設備(STDIN)、標准輸出設備(STDOUT)和標准錯誤輸出設備(STDERR),其文件描述符分別為0、1、2。以后打開的文件的文件描述符分配依次增加。
內核(kernel)利用文件描述符(file descriptor)來訪問文件。文件描述符是非負整數。打開現存文件或新建文件時,內核會返回一個文件描述符。讀寫文件也需要使用文件描述符來指定待讀寫的文件。
系統調用:
在Linux中,為了更好地保護內核,把程序運行空間分為內核空間和用戶空間,它們分別運行在不同的級別上,用戶進程在通常情況下不允許訪問內核數據,也無法使用內核函數。但是在有些情況下,用戶空間的進程需要獲得一定的系統服務,這時,就必須通過系統調用。
系統調用是指操作系統提供給用戶程序調用的一組“特殊”接口,用戶程序可以通過這組接口獲得操作系統內核提供的服務。從邏輯上來說,系統調用可被看成是一個內核與用戶空間程序交互的接口——它好比一個中間人,把用戶進程的請求傳達給內核,待內核把請求處理完畢后再將處理結果送回給用戶空間。
系統調用、用戶編程接口(API)、系統命令、和內核函數的關系
系統調用並非直接和程序員或系統管理員打交道,它僅僅是一個通過軟中斷機制向內核提交請求,獲取內核服務的接口。而在實際使用中程序員調用的多是用戶編程接口——API,而管理員使用的則多是系統命令。
用戶編程接口其實是一個函數定義,說明了如何獲得一個給定的服務,比如read()、malloc()、free()、abs()等。這些系統調用編程接口主要是通過C庫(libc)實現的。
系統命令相對編程接口更高了一層,它是內部引用API的可執行程序,比如我們常用的系統命令ls等。
內核函數和系統調用的關系,內核函數不要想像的過於復雜,其實它們和普通函數很像,只不過在內核實現,因此要滿足一些內核編程的要求。系統調用是一層用戶進入內核的接口,它本身並非內核函數,進入內核后,不同的系統調用會找到對應到各自的內核函數——換個專業說法就叫:系統調用服務服務例程。實際對請求服務的是內核函數而非調用接口。
總而言之,從用戶角度向內核看,依次是系統命令、編程接口、系統調用和內核函數。
系統調用通常通過函數進行調用,通常,用一個負的返回值來表明錯誤,返回一個0值表明成功.
系統調用號:
在Linux中,每個系統調用被賦予一個全局唯一的系統調用號.用戶空間進程執行一個系統調用的時候,這個系統調用號就被用來指明到底要執行哪個系統調用;進程不會提及系統調用的名稱.
系統調用號相當關鍵,一旦分配就不能再有任何變更,否則編譯好的應用程序就會崩潰.此外,如果一個系統調用被刪除,它所占用的系統調用號也不允許被回收利用.
應用程序應該以某種方式通知系統,告訴內核自己需要執行一個系統調用,希望系統切換到內核態,這樣內核就可以代表應用程序來執行該系統調用了.
文件include/asm/unisted.h為每個系統調用規定了唯一的編號。假設用name表示系統調用的名稱,那么系統調用號與系統調用響應函數的關系是:以系統調用號_NR_name作為下標,可找出系統調用表sys_call_table(見arch/i386/kernel/entry.S)中對應表項的內容,它正好 是該系統調用的響應函數sys_name的入口地址。系統調 用表sys_call_table記錄了各sys_name函數在表中的位 置。有了這張表,就很容易根據特定系統調用 在表中的偏移量,找到對應的系統調用響應函數的入口地址。系統調用表共2XX項,余下的項是可供用戶自己添加的系統調用空間。
1.編寫一個系統調用函數,把它加到內核中,函數名以 sys_ 開頭,后跟該系統調用名。如新加系統調用為foo(),在/linux/kernel/sys.c中添加源代碼:
asmlinkage int sys_foo(int x)
{
printk(“%d\n”,x); //注意這是在內核下的,用printk
}
2. 連接新的系統調用
1).inculde/asm/unistd.h在這個文件中加入
#define_NR_foo 238 (因為原列表結尾是237,所以在后面加)
2).are/i386/kernel/entry.S這個文件用來對指針數組初始化,在這個文件中增加一行:
.long SYMBOL_NAME(_sys_foo)
3.重新編譯內核
為了使新的系統調用生效,需要重建Linux內核,以root身份在linux文件夾下編譯內核:
make menuconfig //配置內核,在File system下選中ext3文件系統支持
make clean //清除以前編譯的中間文件
make dep //讀取配置文件,創建依賴關系樹
make bzImage //編譯內核
4.裝載內核,將/linux/arch/i386/boot/bzImage復制到/boot下
修改grub.conf(請看linux啟動配置文件)
重啟系統,在啟動介面上,可以選擇剛才編譯的那個內核進入。
5.編寫測試程序
#include <stdio.h>
#include "/home/xgc/linux/include/asm-i386/unistd.h"
int errno;
_syscall1(char *, foo, int, ret)
int main()
{
int i;
i = 100;
printf("This is the result of new kernel\n");
foo(i);
return 0;
}
在字符介面運行可得到結果:100
宏定義_syscallN()見include/asm/unisted.h)用於系統調用的格式轉換和參數的傳遞。N取0~5之間的整數。 參數個數為N的系統調用由_syscallN()負責格式轉換和參數傳遞。在這里使用了_syscall1()宏指令,宏指令本身在程序中將擴展成名為syscall()的函數,它在main()函數內部加以調用。