Linux0.11中斷及系統呼叫
中斷簡介
Linux0.11使用的Intel i386晶片共有256箇中斷,表現為中斷號0~255.
其中前0~31號中斷已經由Intel預定義,其餘中斷號為可程式設計中斷。
32~47號分別對應linux的16個硬體中斷訊號(包括時鐘、鍵盤、軟盤等)。
0x80中斷即128號中斷為linux系統呼叫軟中斷。
硬中斷和軟中斷
- 軟中斷是執行中斷指令(int n,n為中斷號)主動產生的。而硬中斷則是由外部引發的,具有隨機性、突發性
- 硬中斷中斷號由中斷控制器提供,而軟中斷中斷號直接由指令指出。
系統呼叫
系統呼叫int 0x80是一個軟中斷,是應用程式與Linux核心互動的介面。
系統呼叫初始化過程
在init/main.c初始化主程式main()中呼叫的sched_init()排程初始化函式中最後一行
set_system_gate(0x80,&system_call);
將0x80軟中斷與系統呼叫入口函式system_call聯絡起來。
---------------------------------------------------------------------------------------------------------------------------------------------
init/main.c : main() : line-132 -->kernel/sched.c : sched_init() -->set_system_gate(0x80,&system_call);
------------------------------------------------------------------------------------------------------------------------------------
set_system_gate()
為include/asm目錄中system.h檔案中的巨集函式
#define set_system_gate(n,addr) \ _set_gate(&idt[n],15,3,addr)
#define _set_gate(gate_addr,type,dpl,addr) \ __asm__ ("movw %%dx,%%ax\n\t" \ "movw %0,%%dx\n\t" \ "movl %%eax,%1\n\t" \ "movl %%edx,%2" \ : \ : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ "o" (*((char *) (gate_addr))), \ "o" (*(4+(char *) (gate_addr))), \ "d" ((char *) (addr)),"a" (0x00080000))
該函式即為設定0x80號中斷的中斷描述符,附陷阱門描述符結構
- DPL為優先順序,3為使用者態,0為核心態
- TYPE即為圖中第一行的8~11位
- addr為中斷處理函式地址
- idt為中斷描述符表,共256項,每項佔8位元組,在include/linux/head.h中定義
extern desc_table idt
typedef struct desc_struct { unsigned long a,b; } desc_table[256];
即把system_call函式的地址存入和優先順序、中斷型別的資訊存入中斷描述符表中0x80號中斷
--------------------------------------------------------------------------------------------------------------------------------------------
int 0x80 --> kernel/system_call.s : 80 : _system_call --> include/linux/sys.h : 74 : sys_call_table[eax]()
------------------------------------------------------------------------------------------------------------------------------------
int 0x80在linux0.11中已經封裝好系統呼叫介面,見include/unistd.h檔案133~183行
共_syscall0、_syscall1、_syscall2、_syscall3四個巨集函式,分別為無參~含有三個引數的系統呼叫
這裡貼出無參的系統呼叫_syscall0程式碼
#define _syscall0(type,name) \ type name(void) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ //將輸出賦給變數__res,"=a"表示強制使用ax暫存器 : "0" (__NR_##name)); \ //將__NR_name作為輸入(系統呼叫號,在include/unistd.h定義),"0"表示使用與上一行一樣的暫存器 if (__res >= 0) \ return (type) __res; \ errno = -__res; \ return -1; \ }
舉個呼叫該巨集函式的例子進行講解
在init/main.c:23 呼叫該函式
static inline _syscall0(int,fork)
呼叫該函式的作用為定義了一個
靜態(static) 內聯(inline) 返回型為int(對應巨集函式的type引數) 函式名為fork(對應巨集函式name引數) 的無參函式:static inline int fork(void){}
該函式使用了內聯彙編觸發0x80號中斷
--------------------------------------------------------------------------------------------------------------------------------------------
system_call
為C語言呼叫的彙編函式,在kernel/system_call.s第80行
主要作用為將程序從使用者態切換到核心態,然後呼叫該檔案94行的call _sys_call_table(,%eax,4)
這條指令的含義為跳轉到sys_call_table + eax*4地址去執行。
- sys_call_table為在include/linux/sys.h定義的C語言-函式指標陣列,陣列中存放了72個系統呼叫函式指標。
- eax為中斷號
- *4是因為32位機每個指標佔四個位元組
即實際指令為跳轉到系統呼叫函式指標陣列中執行對應的系統呼叫。
以下為函式指標陣列的初始化
typedef int (*fn_ptr)(); //在include/linux/sched.h 38行
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod, sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount, sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm, sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access, sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir, sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid, sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys, sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit, sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid, sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask, sys_setreuid,sys_setregid };
------------------------------------------------------------------------------------------------------------------------------------
歡迎大家指正、討論