最近有點事情,立刻要開學了,因此學習的腳步就慢下來了。這一篇主要是來講操做系統的系統調用的,像C語言的printf深刻到內部就是一個有關屏幕輸出的系統調用linux
以前提過操做系統是對硬件的抽象,也是軟硬件之間的一層。以前好比若是咱們想要在屏幕上輸出一些字符,就須要一些指令操做,而後把數據放到顯存上。可是在有了操做系統後,就不須要這樣作,也不能這樣作了。這時候只要操做系統提供一個接口來讓咱們完成這個任務git
由操做系統實現提供的全部系統調用所構成的集合即程序接口或應用編程接口(Application Programming Interface,API)。是應用程序同系統之間的接口。github
在硬件設計上,經過區份內核態和用戶態來把內核程序和用戶程序隔離開編程
CS寄存器最低的兩位爲0便是內核態,爲3是用戶態數組
可是系統調用的代碼是處在內核態的,因此就須要提供一種方法來可以讓用戶程序進入內核態來實現系統調用緩存
在X86裏,INT指令就是硬件用來提供由用戶態進入內核態的方法,因此係統調用的實現就能夠變爲:函數
稍微說一下C裏的內聯彙編,以避免以後忘記。學習
gcc的內聯彙編通常都是這個格式spa
asm ( 彙編指令
: 輸出操做數 // 非必需
: 輸入操做數 // 非必需
: 其餘被污染的寄存器 // 非必需
);
複製代碼
第一部分就是彙編指令操作系統
第二部分是輸出操做數,都是 "=?"(var) 的形式, var能夠是任意內存變量(輸出結果會存到這個變量中), ?通常是下面這些標識符 (表示內聯彙編中用什麼來代理這個操做數):
a,b,c,d,S,D 分別表明 eax,ebx,ecx,edx,esi,edi 寄存器 r 上面的寄存器的任意一個(誰閒着就用誰) m 內存 i 當即數(常量,只用於輸入操做數) g 寄存器、內存、當即數 都行 在彙編中用%序號來表明這些輸入/輸出操做數,序號從0開始。爲了與操做數區分開來,寄存器用兩個%引出,如:%%eax
第三部分是是輸入操做數,都是 "?"(var) 的形式, ? 除了能夠是上面的那些標識符,還能夠是輸出操做數的序號,表示用 var 來初始化該輸出操做數,上面的程序中 %0 和 %1 就是一個東西,初始化爲 1(a的值)。
第四部分標出那些在彙編代碼中修改了的、 又沒有在輸入/輸出列表中列出的寄存器, 這樣 gcc 就不會擅自使用這些"危險的"寄存器。 還能夠用 "memory" 表示在內聯彙編中修改了內存, 以前緩存在寄存器中的內存變量須要從新讀取。
#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);
複製代碼
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
複製代碼
_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; \ }
複製代碼
這樣對於一個系統調用就會變成
printf 用戶調用
⬇
int 0x80 庫函數的實現
⬇
進入內核
system_call 中斷調用
⬇
sys_ 系統調用