Linux0.11内核--系统中断处理程序int 0x80实现原理
系統調用是一個軟中斷,中斷號是0x80,它是上層應用程序與Linux系統內核進行交互通信的唯一接口。
這個中斷的設置在kernel/sched.c中441行函數中
void sched_init(void) {int i;struct desc_struct * p;if (sizeof(struct sigaction) != 16)panic("Struct sigaction MUST be 16 bytes");set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));p = gdt+2+FIRST_TSS_ENTRY;for(i=1;i<NR_TASKS;i++) {task[i] = NULL;p->a=p->b=0;p++;p->a=p->b=0;p++;} /* Clear NT, so that we won't have troubles with that later on */__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");ltr(0);lldt(0);outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */outb_p(LATCH & 0xff , 0x40); /* LSB */outb(LATCH >> 8 , 0x40); /* MSB */set_intr_gate(0x20,&timer_interrupt);outb(inb_p(0x21)&~0x01,0x21);set_system_gate(0x80,&system_call); }最后一句就將0x80中斷與system_call(系統調用)聯系起來。
通過int 0x80,就可使用內核資源。不過,通常應用程序都是使用具有標準接口定義的C函數庫間接的使用內核的系統調用,即應用程序調用C函數庫中的函數,C函數庫中再通過int 0x80進行系統調用。
????所以,系統調用過程是這樣的:
????應用程序調用libc中的函數->libc中的函數引用系統調用宏->系統調用宏中使用int 0x80完成系統調用并返回
下面是sys_call_table的定義文件
位于./include/sys.h
extern int sys_setup (); // 系統啟動初始化設置函數。 (kernel/blk_drv/hd.c,71) extern int sys_exit (); // 程序退出。 (kernel/exit.c, 137) extern int sys_fork (); // 創建進程。 (kernel/system_call.s, 208) extern int sys_read (); // 讀文件。 (fs/read_write.c, 55) extern int sys_write (); // 寫文件。 (fs/read_write.c, 83) extern int sys_open (); // 打開文件。 (fs/open.c, 138) extern int sys_close (); // 關閉文件。 (fs/open.c, 192) extern int sys_waitpid (); // 等待進程終止。 (kernel/exit.c, 142) extern int sys_creat (); // 創建文件。 (fs/open.c, 187) extern int sys_link (); // 創建一個文件的硬連接。 (fs/namei.c, 721) extern int sys_unlink (); // 刪除一個文件名(或刪除文件)。 (fs/namei.c, 663) extern int sys_execve (); // 執行程序。 (kernel/system_call.s, 200) extern int sys_chdir (); // 更改當前目錄。 (fs/open.c, 75) extern int sys_time (); // 取當前時間。 (kernel/sys.c, 102) extern int sys_mknod (); // 建立塊/字符特殊文件。 (fs/namei.c, 412) extern int sys_chmod (); // 修改文件屬性。 (fs/open.c, 105) extern int sys_chown (); // 修改文件宿主和所屬組。 (fs/open.c, 121) extern int sys_break (); // (-kernel/sys.c, 21) extern int sys_stat (); // 使用路徑名取文件的狀態信息。 (fs/stat.c, 36) extern int sys_lseek (); // 重新定位讀/寫文件偏移。 (fs/read_write.c, 25) extern int sys_getpid (); // 取進程id。 (kernel/sched.c, 348) extern int sys_mount (); // 安裝文件系統。 (fs/super.c, 200) extern int sys_umount (); // 卸載文件系統。 (fs/super.c, 167) extern int sys_setuid (); // 設置進程用戶id。 (kernel/sys.c, 143) extern int sys_getuid (); // 取進程用戶id。 (kernel/sched.c, 358) extern int sys_stime (); // 設置系統時間日期。 (-kernel/sys.c, 148) extern int sys_ptrace (); // 程序調試。 (-kernel/sys.c, 26) extern int sys_alarm (); // 設置報警。 (kernel/sched.c, 338) extern int sys_fstat (); // 使用文件句柄取文件的狀態信息。(fs/stat.c, 47) extern int sys_pause (); // 暫停進程運行。 (kernel/sched.c, 144) extern int sys_utime (); // 改變文件的訪問和修改時間。 (fs/open.c, 24) extern int sys_stty (); // 修改終端行設置。 (-kernel/sys.c, 31) extern int sys_gtty (); // 取終端行設置信息。 (-kernel/sys.c, 36) extern int sys_access (); // 檢查用戶對一個文件的訪問權限。(fs/open.c, 47) extern int sys_nice (); // 設置進程執行優先權。 (kernel/sched.c, 378) extern int sys_ftime (); // 取日期和時間。 (-kernel/sys.c,16) extern int sys_sync (); // 同步高速緩沖與設備中數據。 (fs/buffer.c, 44) extern int sys_kill (); // 終止一個進程。 (kernel/exit.c, 60) extern int sys_rename (); // 更改文件名。 (-kernel/sys.c, 41) extern int sys_mkdir (); // 創建目錄。 (fs/namei.c, 463) extern int sys_rmdir (); // 刪除目錄。 (fs/namei.c, 587) extern int sys_dup (); // 復制文件句柄。 (fs/fcntl.c, 42) extern int sys_pipe (); // 創建管道。 (fs/pipe.c, 71) extern int sys_times (); // 取運行時間。 (kernel/sys.c, 156) extern int sys_prof (); // 程序執行時間區域。 (-kernel/sys.c, 46) extern int sys_brk (); // 修改數據段長度。 (kernel/sys.c, 168) extern int sys_setgid (); // 設置進程組id。 (kernel/sys.c, 72) extern int sys_getgid (); // 取進程組id。 (kernel/sched.c, 368) extern int sys_signal (); // 信號處理。 (kernel/signal.c, 48) extern int sys_geteuid (); // 取進程有效用戶id。 (kenrl/sched.c, 363) extern int sys_getegid (); // 取進程有效組id。 (kenrl/sched.c, 373) extern int sys_acct (); // 進程記帳。 (-kernel/sys.c, 77) extern int sys_phys (); // (-kernel/sys.c, 82) extern int sys_lock (); // (-kernel/sys.c, 87) extern int sys_ioctl (); // 設備控制。 (fs/ioctl.c, 30) extern int sys_fcntl (); // 文件句柄操作。 (fs/fcntl.c, 47) extern int sys_mpx (); // (-kernel/sys.c, 92) extern int sys_setpgid (); // 設置進程組id。 (kernel/sys.c, 181) extern int sys_ulimit (); // (-kernel/sys.c, 97) extern int sys_uname (); // 顯示系統信息。 (kernel/sys.c, 216) extern int sys_umask (); // 取默認文件創建屬性碼。 (kernel/sys.c, 230) extern int sys_chroot (); // 改變根系統。 (fs/open.c, 90) extern int sys_ustat (); // 取文件系統信息。 (fs/open.c, 19) extern int sys_dup2 (); // 復制文件句柄。 (fs/fcntl.c, 36) extern int sys_getppid (); // 取父進程id。 (kernel/sched.c, 353) extern int sys_getpgrp (); // 取進程組id,等于getpgid(0)。(kernel/sys.c, 201) extern int sys_setsid (); // 在新會話中運行程序。 (kernel/sys.c, 206) extern int sys_sigaction (); // 改變信號處理過程。 (kernel/signal.c, 63) extern int sys_sgetmask (); // 取信號屏蔽碼。 (kernel/signal.c, 15) extern int sys_ssetmask (); // 設置信號屏蔽碼。 (kernel/signal.c, 20) extern int sys_setreuid (); // 設置真實與/或有效用戶id。 (kernel/sys.c,118) extern int sys_setregid (); // 設置真實與/或有效組id。 (kernel/sys.c, 51) // 系統調用函數指針表。用于系統調用中斷處理程序(int 0x80),作為跳轉表。 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 };其中sys_call_table的類型是fn_ptr類型,其中sys_call_table[0]元素為sys_setup,它的類型是fn_ptr類型,它實際上是函數sys_setup的
入口地址。
它的定義如下:
typedef int (*fn_ptr) (); // 定義函數指針類型。
下面的實例代碼有助于理解函數指針:
#include<stdio.h> typedef int (*MyFunc)(); MyFunc Func1; int Func2() {printf("This is a sample output!/n");return 0; } int main() {Func1=Func2;//Func2();//(*Func1)();printf("%x/n",(*Func1));printf("%x/n",Func2);return 0; }system_call系統調用入口函數
#### int 0x80 --linux 系統調用入口點(調用中斷int 0x80,eax 中是調用號)。 .align 2 _system_call: cmpl $nr_system_calls-1,%eax # 調用號如果超出范圍的話就在eax 中置-1 并退出。 ja bad_sys_call push %ds # 保存原段寄存器值。 push %es push %fs pushl %edx # ebx,ecx,edx 中放著系統調用相應的C 語言函數的調用參數。 pushl %ecx # push %ebx,%ecx,%edx as parameters pushl %ebx # to the system call movl $0x10,%edx # set up ds,es to kernel space mov %dx,%ds # ds,es 指向內核數據段(全局描述符表中數據段描述符)。 mov %dx,%es movl $0x17,%edx # fs points to local data space mov %dx,%fs # fs 指向局部數據段(局部描述符表中數據段描述符)。 # 下面這句操作數的含義是:調用地址 = _sys_call_table + %eax * 4。參見列表后的說明。 # 對應的C 程序中的sys_call_table 在include/linux/sys.h 中,其中定義了一個包括72 個 # 系統調用C 處理函數的地址數組表。 call _sys_call_table(,%eax,4) pushl %eax # 把系統調用號入棧。 movl _current,%eax # 取當前任務(進程)數據結構地址??eax。 # 下面97-100 行查看當前任務的運行狀態。如果不在就緒狀態(state 不等于0)就去執行調度程序。 # 如果該任務在就緒狀態但counter[??]值等于0,則也去執行調度程序。 cmpl $0,state(%eax) # state jne reschedule cmpl $0,counter(%eax) # counter je reschedule # 以下這段代碼執行從系統調用C 函數返回后,對信號量進行識別處理。 ret_from_sys_call: # 首先判別當前任務是否是初始任務task0,如果是則不必對其進行信號量方面的處理,直接返回。 # 103 行上的_task 對應C 程序中的task[]數組,直接引用task 相當于引用task[0]。 movl _current,%eax # task[0] cannot have signals cmpl _task,%eax je 3f # 向前(forward)跳轉到標號3。 # 通過對原調用程序代碼選擇符的檢查來判斷調用程序是否是超級用戶。如果是超級用戶就直接 # 退出中斷,否則需進行信號量的處理。這里比較選擇符是否為普通用戶代碼段的選擇符0x000f # (RPL=3,局部表,第1 個段(代碼段)),如果不是則跳轉退出中斷程序。 cmpw $0x0f,CS(%esp) # was old code segment supervisor ? jne 3f # 如果原堆棧段選擇符不為0x17(也即原堆棧不在用戶數據段中),則也退出。 cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? jne 3f # 下面這段代碼(109-120)的用途是首先取當前任務結構中的信號位圖(32 位,每位代表1 種信號), # 然后用任務結構中的信號阻塞(屏蔽)碼,阻塞不允許的信號位,取得數值最小的信號值,再把 # 原信號位圖中該信號對應的位復位(置0),最后將該信號值作為參數之一調用do_signal()。 # do_signal()在(kernel/signal.c,82)中,其參數包括13 個入棧的信息。 movl signal(%eax),%ebx # 取信號位圖??ebx,每1 位代表1 種信號,共32 個信號。 movl blocked(%eax),%ecx # 取阻塞(屏蔽)信號位圖??ecx。 notl %ecx # 每位取反。 andl %ebx,%ecx # 獲得許可的信號位圖。 bsfl %ecx,%ecx # 從低位(位0)開始掃描位圖,看是否有1 的位, # 若有,則ecx 保留該位的偏移值(即第幾位0-31)。 je 3f # 如果沒有信號則向前跳轉退出。 btrl %ecx,%ebx # 復位該信號(ebx 含有原signal 位圖)。 movl %ebx,signal(%eax) # 重新保存signal 位圖信息??current->signal。 incl %ecx # 將信號調整為從1 開始的數(1-32)。 pushl %ecx # 信號值入棧作為調用do_signal 的參數之一。 call _do_signal # 調用C 函數信號處理程序(kernel/signal.c,82) popl %eax # 彈出信號值。 3: popl %eax popl %ebx popl %ecx popl %edx pop %fs pop %es pop %ds iret./include/unistd.h文件中系統調用符號和調用號的對應定義
// 以下是內核實現的系統調用符號常數,用于作為系統調用函數表中的索引值。( include/linux/sys.h ) #define __NR_setup 0 /* used only by init, to get system going */ /* __NR_setup 僅用于初始化,以啟動系統 */ #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 #define __NR_waitpid 7 #define __NR_creat 8 #define __NR_link 9 #define __NR_unlink 10 #define __NR_execve 11 #define __NR_chdir 12 #define __NR_time 13 #define __NR_mknod 14 #define __NR_chmod 15 #define __NR_chown 16 #define __NR_break 17 #define __NR_stat 18 #define __NR_lseek 19 #define __NR_getpid 20 #define __NR_mount 21 #define __NR_umount 22 #define __NR_setuid 23 #define __NR_getuid 24 #define __NR_stime 25 #define __NR_ptrace 26 #define __NR_alarm 27 #define __NR_fstat 28 #define __NR_pause 29 #define __NR_utime 30 #define __NR_stty 31 #define __NR_gtty 32 #define __NR_access 33 #define __NR_nice 34 #define __NR_ftime 35 #define __NR_sync 36 #define __NR_kill 37 #define __NR_rename 38 #define __NR_mkdir 39 #define __NR_rmdir 40 #define __NR_dup 41 #define __NR_pipe 42 #define __NR_times 43 #define __NR_prof 44 #define __NR_brk 45 #define __NR_setgid 46 #define __NR_getgid 47 #define __NR_signal 48 #define __NR_geteuid 49 #define __NR_getegid 50 #define __NR_acct 51 #define __NR_phys 52 #define __NR_lock 53 #define __NR_ioctl 54 #define __NR_fcntl 55 #define __NR_mpx 56 #define __NR_setpgid 57 #define __NR_ulimit 58 #define __NR_uname 59 #define __NR_umask 60 #define __NR_chroot 61 #define __NR_ustat 62 #define __NR_dup2 63 #define __NR_getppid 64 #define __NR_getpgrp 65 #define __NR_setsid 66 #define __NR_sigaction 67 #define __NR_sgetmask 68 #define __NR_ssetmask 69 #define __NR_setreuid 70 #define __NR_setregid 71這是一系列宏,它們的定義在unistd.h中,基本形式為#define _NR_name value,name為系統函數名字,value是一個整數值,是name所對應的系統函數指針在sys_call_table中的偏移量。
?
系統調用宏也在本文件內定義,采用內聯匯編,如下:
// 以下定義系統調用嵌入式匯編宏函數。 // 不帶參數的系統調用宏函數。type name(void)。 // %0 - eax(__res),%1 - eax(__NR_##name)。其中name 是系統調用的名稱,與 __NR_ 組合形成上面 // 的系統調用符號常數,從而用來對系統調用表中函數指針尋址。 // 返回:如果返回值大于等于0,則返回該值,否則置出錯號errno,并返回-1。 #define _syscall0(type,name) / type name(void) / { / long __res; / __asm__ volatile ( "int $0x80" / // 調用系統中斷0x80。 :"=a" (__res) / // 返回值??eax(__res)。 :"" (__NR_##name)); / // 輸入為系統中斷調用號__NR_name。if (__res >= 0) / // 如果返回值>=0,則直接返回該值。return (type) __res; errno = -__res; / // 否則置出錯號,并返回-1。return -1;} // 有1 個參數的系統調用宏函數。type name(atype a) // %0 - eax(__res),%1 - eax(__NR_name),%2 - ebx(a)。 #define _syscall1(type,name,atype,a) / type name(atype a) / { / long __res; / __asm__ volatile ( "int $0x80" / : "=a" (__res) / : "" (__NR_##name), "b" ((long)(a))); / if (__res >= 0) / return (type) __res; / errno = -__res; / return -1; / } // 有2 個參數的系統調用宏函數。type name(atype a, btype b) // %0 - eax(__res),%1 - eax(__NR_name),%2 - ebx(a),%3 - ecx(b)。 #define _syscall2(type,name,atype,a,btype,b) / type name(atype a,btype b) / { / long __res; / __asm__ volatile ( "int $0x80" / : "=a" (__res) / : "" (__NR_##name), "b" ((long)(a)), "c" ((long)(b))); / if (__res >= 0) / return (type) __res; / errno = -__res; / return -1; / } // 有3 個參數的系統調用宏函數。type name(atype a, btype b, ctype c) // %0 - eax(__res),%1 - eax(__NR_name),%2 - ebx(a),%3 - ecx(b),%4 - edx(c)。 #define _syscall3(type,name,atype,a,btype,b,ctype,c) / type name(atype a,btype b,ctype c) / { / long __res; / __asm__ volatile ( "int $0x80" / : "=a" (__res) / : "" (__NR_##name), "b" ((long)(a)), "c" ((long)(b)), "d" ((long)(c))); / if (__res>=0) / return (type) __res; / errno=-__res; / return -1; / }與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的Linux0.11内核--系统中断处理程序int 0x80实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个操作系统的实现(1):分析linux
- 下一篇: c语言time_t转oletime,Da