例項分析系統呼叫過程

2021-08-26 14:49:08 字數 3273 閱讀 1376

我們由剛開始接觸程式設計的乙個程式說起。

上述一段**,通過gcc編譯,生成乙個.o檔案,執行,就能輸出printf函式要輸出的文字。那麼,對於printf()函式這個呼叫,作業系統到底發生了什麼?

為了方便管理,我們將計算機系統資源分為使用者態和核心態,對於核心部分,一般是對程序資源進行管理,而這些東西是不允許直接操作的,所有對這些資源的訪問都必須有作業系統控制。在linux中,系統呼叫(system call)是使用者空間訪問核心的唯一手段,除了異常和陷入外,它是核心唯一的合法入口。

那麼怎麼樣才能引起系統呼叫呢?即怎麼從使用者態來到核心態?一般來說,系統呼叫都是通過軟體中斷實現的,在x86系統上的軟體中斷是由int 0x80指令引起的,而128號異常處理程式就是系統呼叫處理程式system_call()。

有了以上知識,我們再做以下幾點說明。

1、將核心程式和使用者程式隔離,即使用者態和核心態,這是由硬體來實現的,這個我們在講記憶體的時候經常會提到,比如某段記憶體不能隨便訪問,某段記憶體僅僅只讓作業系統訪問。

2、那麼怎麼知道當前程式執行在什麼態呢?由於cs:ip是指向需要執行的指令的位址的,那麼我們用cs的低兩位來表示,其中0是核心態,3是使用者態。

3、核心態可以訪問任何資料,使用者態不能訪問核心態資料。

4、這裡還介紹兩個重要的引數,dpl即描述目標記憶體段的特權級,cpl(cs的低兩位)表示當前的特權級,只有當cpl<=dpl時,才能執行當前指令。注意,初始化的時候dpl=0。

對於我們文章開始提到的printf()函式的呼叫過程大致如下所示。下面我們將分析整個呼叫過程。

對於任何使用者程式,它最終都有一段包含int指令的**;作業系統通過寫中斷處理,獲取想要呼叫程式的編號;作業系統根據編號執行相應的**。

對於printf()來說,最終展開成包含int指令的**,如下。

linux/lib/write.c

_syscall3(int,write,int,fd,const,char*,buf,off_t,count) //3表示引數為3個,此次呼叫是庫函式呼叫,注意第乙個是函式名,展開就是int write(int fd,const char * buf,off_t count)

linux/include/unistd.h

#define _syscall3(type,name,atype,a,btype,b,ctype,c)

type name(atype a,btype b,ctype c)

//此段**為內嵌彙編,就是將_nr_write(即系統呼叫號)放在eax中。

而_nr_write的系統呼叫號定義下面的函式中。

上面說的就是將系統呼叫號賦值給eax,然後呼叫int 0x80。那麼int 0x80做了什麼?

前面說過中斷是通過itd表來找處理函式,然後執行,itd表如下所示。

//該函式用來設定0x80中斷處理

在linux/include/asm/system.h中

#define set_system_gate(n,addr) //n為中斷號,addr為中斷位址

_set_gate(&idt[n],15,3,addr); //idt是中斷向量表基址

#define _set_gate(gate_addr,type,dpl,addr) //dpl=3,當dpl置3之後,cpl就會滿足小於等於dpl了

__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」(*(4+(char *)(gate_addr))),

」d」((char *)(addr),

」a」(0c00080000))

當上面dpl=3之後,就能進入80中斷了,此時cs=8,ip=system_call。當cs等於8的時候,cpl=0,那麼就完全滿足cpl<=dpl了,那麼就能進入核心了。說白了int 0x80就是讓cs:ip進入到核心,可以在核心中執行。

在linux/kernel/system_call.s中

nr_system_calls=72

.globl _system_call

_system_call:cmpl $nr_system_calls-1,%eax //eax存放的是系統呼叫號

ja bad_sys_call

push %ds

push %es

push %fs

pushl %eax

pushl %ecx

pushl %ebx

movl $0x10,%edx

mov %dx,%ds

mov %dx,%es

movl $0x17,%edx

mov %dx,%fs

call _sys_call_table(,%eax,4) //a(,%eax,4)=a+4*eax,

pushl %eax //返回值壓棧,留給ret_from_sys_call返回時使用

......

ret_from_sys_call:

popl %eax

......

iret

_sys_call_table+4*%eax就是相應的系統呼叫處理函式入口,由於每個系統呼叫對應的函式占用4個位元組,所以*4。其中_sys_call_table就是sys_call_table的基位址,也就是指向那個表的第0個系統呼叫,而%eax中存放的是4,那麼最終就指向sys_write了。所以整個呼叫過程如下。

系統呼叫過程詳解

整個過程如下 首先指令流執行到系統呼叫函式時,系統呼叫函式通過int 0x80指令進入系統呼叫入口程式,並且把系統呼叫號放入 eax中,如果需要傳遞引數,則把引數放入 ebx,ecx和 edx中。進入系統呼叫入口程式 system call 後,它首先把相關的暫存器壓入核心堆疊 以備將來恢復 這個過...

fork系統呼叫過程

又是查詢資料,又是看原始碼,折騰了大半天,終於把fork的過程弄完了,但是後面的跟蹤狀態還不太懂,等具體後面弄清楚了,再加上。核心是2.6.11版本的。fork 系統呼叫 我們執行乙個系統呼叫時,系統將呼叫巨集指令 syscall0 define syscall0 type,name type na...

Linux系統呼叫過程

linux系統分為核心空間和使用者空間 應用程式通過引發乙個異常來促使系統切換到核心狀態 去執行系統呼叫的處理函式 對於ia 32體系 通過int 0x80指令觸發該中斷 具體可參考gun庫 sysdeps unix sysv linux arch syscall.s 當系統呼叫 open read...