Linux0.11核心系列—2.系統調用機制分析,linux0.11系統調用

來源:互聯網
上載者:User

Linux0.11核心系列—2.系統調用機制分析,linux0.11系統調用

【著作權,轉載請註明出處。出處:http://www.cnblogs.com/joey-hua/p/5570691.html 】

 

Linux核心從啟動到初始化也看了好些個源碼檔案了,這次看到kernel檔案夾下的system_call.s,這個檔案主要就是系統調用的過程。但說到系統調用,不只是這一個檔案這麼簡單,裡面牽扯到的內容太多,這裡就做個筆記記錄一下從建立中斷到最終調用系統調用的完整機制。

假設就從write這個函數作為系統調用來解釋。

系統調用的本質就是使用者進程需要訪問核心層級的代碼,但使用者進程的許可權是最低的,核心代碼是許可權最高的,不允許直接存取,需要通過中斷門作為媒介來實現許可權的跳轉。簡單講就是使用者進程調用一個中斷,這個中斷再去訪問核心代碼。這裡就來學習一下Linux核心具體是怎麼做的。

1.建立中斷描述符表IDT

因為要用到中斷,所以首先要建立中斷描述符表IDT,作用如:

在head.s檔案中,建立好了IDT,比如要使用int 0x80,就從_idt開始找到位移為0x80的地方執行代碼。

.align 3# 按8 位元組方式對齊記憶體位址邊界。_idt:.fill 256,8,0# idt is uninitialized# 256 項,每項8 位元組,填0。idt_descr:#下面兩行是lidt 指令的6 位元組運算元:長度,基址。.word 256*8-1# idt contains 256 entries.long _idtlidt idt_descr# 載入中斷描述符表寄存器值。

2.建立0x80號中斷

所有的系統調用都是通過0x80號中斷來實現的,所以接下來就是建立第0x80號中斷,在sched.c中:

// 設定系統調用中斷門。  set_system_gate (0x80, &system_call);

這裡通過set_system_gate這個宏定義就把0x80中斷和函數system_call關聯上了,這裡先不管system_call,先看set_system_gate,在system.h中:

//// 設定系統調用門函數。// 參數:n - 中斷號;addr - 中斷程式位移地址。// &idt[n]對應中斷號在中斷描述符表中的位移值;中斷描述符的類型是15,特權級是3。#define set_system_gate(n,addr) _set_gate(&idt[n],15,3,addr)//// 設定門描述符宏函數。// 參數:gate_addr -描述符地址;type -描述符中類型域值;dpl -描述符特權層值;addr -位移地址。// %0 - (由dpl,type 組合成的類型標誌字);%1 - (描述符低4 位元組地址);// %2 - (描述符高4 位元組地址);%3 - edx(程式位移地址addr);%4 - eax(高字中含有段選擇符)。#define _set_gate(gate_addr,type,dpl,addr) \__asm__ ( "movw %%dx,%%ax\n\t" \// 將位移地址低字與選擇符組合成描述符低4 位元組(eax)。  "movw %0,%%dx\n\t" \// 將類型標誌字與位移高字組合成描述符高4 位元組(edx)。  "movl %%eax,%1\n\t" \// 分別設定門描述符的低4 位元組和高4 位元組。"movl %%edx,%2"::"i" ((short) (0x8000 + (dpl << 13) + (type << 8))),  "o" (*((char *) (gate_addr))),  "o" (*(4 + (char *) (gate_addr))), "d" ((char *) (addr)), "a" (0x00080000))

這裡參考中斷門結構圖可知,這裡設定特權級是3,使用者進程也是3,就可以直接存取此中斷,位移地址對應的上面的system_call,也就是說如果調用中斷int 0x80,那麼就會去訪問system_call函數。注意這裡的n就是0x80,也就是idt數組的[0x80],idt在head.h中聲明,編譯後會變成符號_idt,在head.s中定義的,就此關聯上。

3.聲明系統調用函數

以write系統函數為例,在write.c中聲明此函數:

_syscall3 (int, write, int, fd, const char *, buf, off_t, count)

_syscall3又是一個宏定義,在unistd.h中:

// 有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; \}

所以翻譯過來就是在write.c中可以寫成:

int write(int fd,const char* buf,off_t count) \{ \long __res; \__asm__ volatile ( "int $0x80" \: "=a" (__res) \: "" (__NR_write), "b" ((long)(fd)), "c" ((long)(buf)), "d" ((long)(count))); \if (__res>=0) \return (type) __res; \errno=-__res; \return -1; \}

是不是一下子就清晰明朗了,也就是說,如果一個使用者進程要使用write函數,就會去調用int 0x80中斷,然後把三個參數fd、buf、count分別存入ebx、ecx、edx寄存器,還有個最關鍵的是_NR_write,會把這個值存入eax寄存器,具體做什麼用等會再說,這個是在unistd.h中定義的:

#define __NR_write 4

好,現在各種初始化和聲明都完成了,萬事俱備只欠東風!

4.系統調用過程

使用者進程調用函數write,就會調用int 0x80中斷,上面第2點已經說了,如果調用中斷int 0x80會去訪問system_call函數,sched.c:

extern int system_call (void);// 系統調用中斷處理常式(kernel/system_call.s,80)。

是在system_call中定義,注意編譯後頭部會加上_,以下代碼只截取了前半部分:

_system_call:cmpl $nr_system_calls-1,%eax # 調用號如果超出範圍的話就在eax 中置-1 並退出。ja bad_sys_callpush %ds # 儲存原段寄存器值。push %espush %fspushl %edx # ebx,ecx,edx 中放著系統調用相應的C 語言函數的調用參數。pushl %ecx # push %ebx,%ecx,%edx as parameterspushl %ebx # to the system callmovl $0x10,%edx # set up ds,es to kernel spacemov %dx,%ds # ds,es 指向核心資料區段(通用描述元表中資料區段描述符)。mov %dx,%esmovl $0x17,%edx # fs points to local data spacemov %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。

注意從pushl %edx開始的三句代碼,是前面第3點提到的三個參數依次從右向左入棧。重點是call _sys_call_table(,%eax,4)這句代碼,翻譯過來就是call [eax*4 + _sys_call_table],根據第3點,eax存的是_NR_write的值也就是4,因為_sys_call_table是sys.h中的一個int (*)()類型的數組,裡面存的是所有的系統調用函數地址,所以再翻譯一下就是訪問sys_call_table[4]也就是sys_write函數:

// 系統調用函數指標表。用於系統調用中斷處理常式(int 0x80),作為跳轉表。fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,  sys_write, ...}

sys_write在fs下的read_write.c:

intsys_write (unsigned int fd, char *buf, int count){  struct file *file;  struct m_inode *inode;...}

好了,到這裡為止才明白千迴百轉最終調用的就是這個sys_write函數。至此分析結束!

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.