前言
最近有點事情,馬上要開學了,所以學習的腳步就慢下來了。這一篇主要是來說操作系統(tǒng)的系統(tǒng)調用的,像C語言的printf深入到內部就是一個有關屏幕輸出的系統(tǒng)調用
什么是系統(tǒng)調用
之前提過操作系統(tǒng)是對硬件的抽象,也是軟硬件之間的一層。之前比如如果我們想要在屏幕上輸出一些字符,就需要一些指令操作,然后把數(shù)據(jù)放到顯存上。但是在有了操作系統(tǒng)后,就不需要這樣做,也不能這樣做了。這時候只要操作系統(tǒng)提供一個接口來讓我們完成這個任務
由操作系統(tǒng)實現(xiàn)提供的所有系統(tǒng)調用所構成的集合即程序接口或應用編程接口(Application Programming
Interface,API)。是應用程序同系統(tǒng)之間的接口。
系統(tǒng)調用的實現(xiàn)
在硬件設計上,通過區(qū)分內核態(tài)和用戶態(tài)來把內核程序和用戶程序隔離開
CS寄存器最低的兩位為0即是內核態(tài),為3是用戶態(tài)
但是系統(tǒng)調用的代碼是處在內核態(tài)的,所以就需要提供一種方法來能夠讓用戶程序進入內核態(tài)來實現(xiàn)系統(tǒng)調用
在X86里,INT指令就是硬件用來提供由用戶態(tài)進入內核態(tài)的方法,所以系統(tǒng)調用的實現(xiàn)就可以變?yōu)椋?br>
* 由用戶程序發(fā)起一個INT指令,指明要調用的服務
* 在操作系統(tǒng)里寫出相應的中斷處理
* 由操作系統(tǒng)根據(jù)用戶指明要調用的服務取執(zhí)行相應的代碼
內聯(lián)匯編
稍微說一下C里的內聯(lián)匯編,以免之后忘記。
gcc的內聯(lián)匯編一般都是這個格式
asm ( 匯編指令 : 輸出操作數(shù) // 非必需 : 輸入操作數(shù) // 非必需 : 其他被污染的寄存器 // 非必需 );
*
第一部分就是匯編指令
*
第二部分是輸出操作數(shù),都是 "=?"(var) 的形式, var可以是任意內存變量(輸出結果會存到這個變量中), ?一般是下面這些標識符
(表示內聯(lián)匯編中用什么來代理這個操作數(shù)):
a,b,c,d,S,D 分別代表 eax,ebx,ecx,edx,esi,edi 寄存器
r 上面的寄存器的任意一個(誰閑著就用誰)
m 內存
i 立即數(shù)(常量,只用于輸入操作數(shù))
g 寄存器、內存、立即數(shù) 都行
在匯編中用%序號來代表這些輸入/輸出操作數(shù),序號從0開始。為了與操作數(shù)區(qū)分開來,寄存器用兩個%引出,如:%%eax
*
第三部分是是輸入操作數(shù),都是 "?"(var) 的形式, ? 除了可以是上面的那些標識符,還可以是輸出操作數(shù)的序號,表示用 var
來初始化該輸出操作數(shù),上面的程序中 %0 和 %1 就是一個東西,初始化為 1(a的值)。
*
第四部分標出那些在匯編代碼中修改了的、 又沒有在輸入/輸出列表中列出的寄存器, 這樣 gcc 就不會擅自使用這些"危險的"寄存器。 還可以用
"memory" 表示在內聯(lián)匯編中修改了內存, 之前緩存在寄存器中的內存變量需要重新讀取。
參考鏈接 <https://github.com/1184893257/simplelinux/blob/master/inlineasm.md>
Linux0.11里對系統(tǒng)調用的代碼實現(xiàn)
設置中斷
* 首先需要對IDT設置中斷調用的處理函數(shù) #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)) set_system_gate(0x80,&system_call);
實現(xiàn)中斷函數(shù)
* sys_call_table[]是一個指針數(shù)組,定義在include/linux/sys.h中,該指針數(shù)組中設置了所有72個系統(tǒng)調用C處理函數(shù)地址。
system_call: cmpl $nr_system_calls-1,%eax ja bad_sys_call push %ds push %es
push %fs pushl %edx 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
mov %dx,%es movl $0x17,%edx # fs points to local data space mov %dx,%fs call
sys_call_table(,%eax,4) pushl %eax movl current,%eax cmpl $0,state(%eax) #
state jne reschedule cmpl $0,counter(%eax) # counter je reschedule
提供接口
* 在linux向應用程序提供系統(tǒng)調用接口write
* _syscall3的本質上是一個宏 _syscall3(int,write,int,fd,const char *,buf,off_t,count)
#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) \
: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \ if
(__res>=0) \ return (type) __res; \ errno=-__res; \ return -1; \ }
小結
這樣對于一個系統(tǒng)調用就會變成
printf 用戶調用
?
int 0x80 庫函數(shù)的實現(xiàn)
?
進入內核
system_call 中斷調用
?
sys_ 系統(tǒng)調用
熱門工具 換一換