系統(tǒng)調(diào)用是什么,?,??
系統(tǒng)調(diào)用是操作系統(tǒng)為用戶提供的一系列API,;系統(tǒng)調(diào)用將用戶的請(qǐng)求發(fā)給內(nèi)核,,內(nèi)核執(zhí)行完以后,將結(jié)果返回給用戶,;
以open為例,,進(jìn)行系統(tǒng)調(diào)用:
分析Linux2.6.11版本
<1>通過在unistd.h中的函數(shù)名的拼接;
<2>找到對(duì)應(yīng)的系統(tǒng)調(diào)用號(hào)
<3>然后將此系統(tǒng)調(diào)用號(hào)通過eax寄存器告知內(nèi)核,在執(zhí)行0x80號(hào)中斷的時(shí)候,,eax寄存器中放的是5,;
<4>將用戶空間的esp等信息壓入內(nèi)核棧,在內(nèi)核棧上執(zhí)行系統(tǒng)調(diào)用,;
<5>系統(tǒng)調(diào)用執(zhí)行完畢,,將結(jié)果寫入eax寄存器中,將用戶空間的信息彈出內(nèi)核棧,,執(zhí)行用戶空間的代碼,;
系統(tǒng)調(diào)用時(shí)的當(dāng)前進(jìn)程要進(jìn)行模式的切換,要從用戶態(tài)切換到內(nèi)核態(tài),,那么用戶態(tài)的一些寄存器信息,,就要進(jìn)行壓棧的操作,壓入內(nèi)核棧,,以免下次進(jìn)行現(xiàn)場(chǎng)恢復(fù)(恢復(fù)到用戶空間)的時(shí)候,,直接從內(nèi)核棧彈出寄存器的信息,并讓eax寄存器帶上內(nèi)核完成系統(tǒng)調(diào)用時(shí)的結(jié)果,;
進(jìn)程切換來了,。。,。
當(dāng)我們進(jìn)程進(jìn)行切換的時(shí)候,,用戶態(tài)的寄存器信息,是保存在哪里的呢,?用戶態(tài)的信息是保存在task_struct里面的成員變量thread里面,;
struct task_struct {
/**
* 進(jìn)程狀態(tài)。
*/
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
/**
* 進(jìn)程的基本信息,。
*/
struct thread_info *thread_info;
atomic_t usage;
unsigned long flags; /* per process flags, defined below */
unsigned long ptrace;
int lock_depth; /* Lock depth */
/**
* 進(jìn)行的動(dòng)態(tài)優(yōu)先權(quán)和靜態(tài)優(yōu)先權(quán)
*/
int prio, static_prio;
/**
* 進(jìn)程所在運(yùn)行隊(duì)列,。每個(gè)優(yōu)先級(jí)對(duì)應(yīng)一個(gè)運(yùn)行隊(duì)列。
*/
struct list_head run_list;
/**
* mm:指向內(nèi)存區(qū)描述符的指針
*/
struct mm_struct *mm, *active_mm;
struct thread_struct thread;
...
}
針對(duì)上面的代碼,,我們來說一下thread和thread_info的區(qū)別,;
以下為thread的結(jié)構(gòu):
/**
* 進(jìn)程被切換出去后,內(nèi)核把它的硬件上下文保存在這個(gè)結(jié)構(gòu)中,。
* 它包含大部分CPU寄存器,,但是不包含eax、ebx這樣的寄存器,。
*/
struct thread_struct {
/* cached TLS descriptors. */
struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
unsigned long esp0;
unsigned long sysenter_cs;
unsigned long eip;
unsigned long esp;
unsigned long fs;
unsigned long gs;
/* Hardware debugging registers */
unsigned long debugreg[8]; /* %%db0-7 debug registers */
/* fault info */
unsigned long cr2, trap_no, error_code;
/* floating point info */
/**
* 為支持選擇性裝入FPU,、MMX和XMM寄存器,引入此結(jié)構(gòu),。
* 當(dāng)切換進(jìn)程時(shí),,將進(jìn)程的這些寄存器保存在i387結(jié)構(gòu)中,。
*/
union i387_union i387;
/* virtual 86 mode info */
struct vm86_struct __user * vm86_info;
unsigned long screen_bitmap;
unsigned long v86flags, v86mask, saved_esp0;
unsigned int saved_fs, saved_gs;
/* IO permissions */
unsigned long *io_bitmap_ptr;
/* max allowed port in the bitmap, in bytes: */
unsigned long io_bitmap_max;
};
以上代碼可以看出來我們的thread里面保存的是一些進(jìn)程切換時(shí),寄存器中的內(nèi)容,,當(dāng)進(jìn)行進(jìn)程切換的時(shí)候,,我們需要保存寄存器的信息,把寄存器的信息賦值給thread里面的各個(gè)變量,;
thread_info的結(jié)構(gòu)如下:
struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
/**
* 如果有TIF_NEED_RESCHED標(biāo)志,,則必須調(diào)用調(diào)度程序。
*/
unsigned long flags; /* low level flags */
/**
* 線程標(biāo)志:
* TS_USEDFPU:表示進(jìn)程在當(dāng)前執(zhí)行過程中,,是否使用過FPU,、MMX和XMM寄存器。
*/
unsigned long status; /* thread-synchronous flags */
/**
* 可運(yùn)行進(jìn)程所在運(yùn)行隊(duì)列的CPU邏輯號(hào),。
*/
__u32 cpu; /* current CPU */
__s32 preempt_count; /* 0 => preemptable, <0 => BUG */
mm_segment_t addr_limit; /* thread address space:
0-0xBFFFFFFF for user-thead
0-0xFFFFFFFF for kernel-thread
*/
struct restart_block restart_block;
unsigned long previous_esp; /* ESP of the previous stack in case
of nested (IRQ) stacks
*/
__u8 supervisor_stack[0];
};
thread_info是保存不同體系下進(jìn)程的信息,,不同的體系結(jié)構(gòu)可能進(jìn)程需要存儲(chǔ)的信息不盡相同, 這就需要我們實(shí)現(xiàn)一種通用的方式, 我們將體系結(jié)構(gòu)相關(guān)的部分和無關(guān)的部門進(jìn)行分離,這就是thread_info的作用,;
是不是會(huì)有小伙伴會(huì)問那當(dāng)我一個(gè)進(jìn)程進(jìn)行系統(tǒng)調(diào)用的時(shí)候,,其實(shí)我并不知道,當(dāng)我的系統(tǒng)調(diào)用返回的時(shí)候,,會(huì)不會(huì)發(fā)生進(jìn)程切換,,我進(jìn)程系統(tǒng)調(diào)用前是不知道的,那么我們?yōu)槭裁催M(jìn)行模式切換時(shí),,要把用戶的棧信息,,壓入內(nèi)核棧呢?我們?yōu)槭裁床恢苯颖4嬖趖hread里面,,這樣的話,,當(dāng)系統(tǒng)調(diào)用返回的時(shí)候,如果剛好又要進(jìn)行切換的時(shí)候,,我們的用戶棧信息,,已經(jīng)保存在thread里面了,不就好了么,?,??
其實(shí),,對(duì)于這個(gè)問題,我自己也是很郁悶,,通過問蘇老師,,我終于才懂了;
蘇大神說:當(dāng)進(jìn)行系統(tǒng)調(diào)用的時(shí)候,,當(dāng)前進(jìn)程的時(shí)間片就停止了,,由內(nèi)核去完成系統(tǒng)調(diào)用,,當(dāng)我們系統(tǒng)調(diào)用返回的時(shí)候,時(shí)間片就可以重新開始計(jì)時(shí),,無論怎么樣,,系統(tǒng)調(diào)用以后,一定是有時(shí)間讓內(nèi)核把信息,,返回給用戶,,系統(tǒng)調(diào)用的結(jié)果會(huì)保存在eax寄存器中,如果系統(tǒng)剛調(diào)用完畢,,準(zhǔn)備返回用戶空間,,就在此時(shí),發(fā)生了進(jìn)程切換,,那么別的進(jìn)程就會(huì)改掉eax的值,,那么當(dāng)我下次進(jìn)行恢復(fù)的時(shí)候,還得重新調(diào)用系統(tǒng)調(diào)用,,這樣子的話,,對(duì)于系統(tǒng)的資源就態(tài)浪費(fèi)了;所以說進(jìn)行系統(tǒng)調(diào)用的返回值一定是返回給用戶的,,當(dāng)用戶吧eax寄存器的值保存起來以后,,標(biāo)志這系統(tǒng)調(diào)用完畢,接下來切不切換進(jìn)程就沒有啥影響了,;
當(dāng)發(fā)生系統(tǒng)調(diào)用或者中斷的時(shí)候,,中斷發(fā)生前夕,要把所有相關(guān)寄存器的內(nèi)容都保存在內(nèi)核堆棧中,,由宏SAVE_ALL宏完成 :
#define SAVE_ALL \
cld; \
pushl %es; \
pushl %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
movl $(__KERNEL_DS),%edx; \
movl %edx,%ds; \
movl %edx,%es;
恢復(fù)現(xiàn)場(chǎng)的宏RESTORE_ALL :
從中斷返回時(shí),,恢復(fù)相關(guān)寄存器的內(nèi)容,這是通過RESTORE_ALL宏完成的:
#define RESTORE_ALL \
popl %ebx; \
popl %ecx; \
popl %edx; \
popl %esi; \
popl %edi; \
popl %ebp; \
popl %eax; \
1: popl %ds; \
2: popl %es; \
addl $4,%esp; \
3: iret;
總結(jié):系統(tǒng)調(diào)用的時(shí)候,,用戶態(tài)的棧信息壓入內(nèi)核棧,;進(jìn)程切換的時(shí)候,用戶態(tài)的棧信息保存在task_struct中的成員thread中,;
|