上周的試驗(yàn)中,我選擇的系統(tǒng)調(diào)用號(hào)是34號(hào), 處理函數(shù)為sys_dup.
匯編方式的調(diào)用如下:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int new_fildes=0;
char *szTemp="test_dup\n";
asm volatile(
"mov $1,%%ebx\n\t" //傳遞參數(shù)
"mov $41,%%eax\n\t" //設(shè)置系統(tǒng)調(diào)用號(hào)
"int $0x80\n\t" //進(jìn)行軟中斷觸發(fā)
"mov %%eax,%0\n\t" //保存返回值
:"=m"(new_fildes)
);
printf("new fildes: %d\n",new_fildes);
write(new_fildes,szTemp,strlen(szTemp));
return 0;
}
修改 MenuOS中的 test.c, 加入自己的命令. 大致如下:
int Time(int argc, char *argv[])
{
time_t tt;
struct tm *t;
tt = time(NULL);
t = localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
return 0;
}
int TimeAsm(int argc, char *argv[])
{
time_t tt;
struct tm *t;
asm volatile(
"mov $0,%%ebx\n\t"
"mov $0xd,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=m" (tt)
);
t = localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
return 0;
}
int Dup(int argc, char *argv[]){
int new_fildes=dup(1);
char *szTemp="test_dup\n";
printf("new fildes: %d\n",new_fildes);
write(new_fildes,szTemp,strlen(szTemp));
return 0;
}
int DupAsm(int argc, char *argv[]){
int new_fildes=0;
char *szTemp="test_dup\n";
asm volatile(
"mov $1,%%ebx\n\t"
"mov $41,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
:"=m"(new_fildes)
);
printf("new fildes: %d\n",new_fildes);
write(new_fildes,szTemp,strlen(szTemp));
return 0;
}
int main()
{
PrintMenuOS();
SetPrompt("MenuOS>>");
MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL);
MenuConfig("quit","Quit from MenuOS",Quit);
MenuConfig("time","Show System Time",Time);
MenuConfig("time-asm","Show System Time(asm)",TimeAsm);
MenuConfig("dup","dup the stdout fildes",Dup);
MenuConfig("dup-asm","dup the stdout fildes",DupAsm);
ExecuteMenu();
}
修改后的 Menu OS 運(yùn)行效果如下圖所示:

跟蹤 sys_dup的執(zhí)行過程
我們?cè)趩?dòng)內(nèi)核的時(shí)候,暫時(shí)‘凍住’CPU。
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
啟動(dòng)另一個(gè)終端,輸入如下命令,建立調(diào)試關(guān)系.
gdb
file linux-3.18.6/vmlinux 加載調(diào)試用的符號(hào)表
target remote:1234
c
b sys_dup
如下圖,停止在sys_dup處.

執(zhí)行完 sys_dup后進(jìn)入 schedule()進(jìn)行進(jìn)程調(diào)度:

/linux-3.18.6/arch/x86/kernel/entry_32.S
系統(tǒng)調(diào)用代碼入口如下:
489 # system call handler stub
490ENTRY(system_call)
491 RING0_INT_FRAME # can't unwind into user space anyway
492 ASM_CLAC
493 pushl_cfi %eax # save orig_eax
494 SAVE_ALL
495 GET_THREAD_INFO(%ebp)
496 # system call tracing in operation / emulation
497 testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
498 jnz syscall_trace_entry
499 cmpl $(NR_syscalls), %eax
500 jae syscall_badsys
501syscall_call:
502 call *sys_call_table(,%eax,4)
503syscall_after_call:
504 movl %eax,PT_EAX(%esp) # store the return value
505syscall_exit:
系統(tǒng)調(diào)用是0x80號(hào)軟中斷而引發(fā)的調(diào)用,0x80號(hào)中斷的處理程序是system_call,也就是上面所列的代碼。當(dāng)檢測(cè)到系統(tǒng)調(diào)用發(fā)生時(shí)(int 0x80中斷),第一步先保存現(xiàn)場(chǎng),通過一個(gè)宏指令SAVE_ALL實(shí)現(xiàn)的,這個(gè)指令是把寄存器的狀態(tài)通過壓棧的方式保存起來。
然后會(huì)調(diào)用sys_call_table,通過eax寄存器的值查找系統(tǒng)調(diào)用表,找到幾號(hào)系統(tǒng)調(diào)用,然后調(diào)用相應(yīng)的系統(tǒng)調(diào)用。
當(dāng)系統(tǒng)調(diào)用完成時(shí),內(nèi)核會(huì)檢測(cè)一些情況,比如進(jìn)程的切換或者信號(hào)發(fā)生什么的。如果發(fā)生了這些情況,內(nèi)核會(huì)轉(zhuǎn)向syscall_exit_work中執(zhí)行.
系統(tǒng)調(diào)用處理的流程圖如下:

總結(jié)
本次實(shí)驗(yàn)使用GDB跟蹤了系統(tǒng)調(diào)用執(zhí)行的整個(gè)過程,同時(shí)又從源代碼級(jí)別比較深入的了解到完成系統(tǒng)調(diào)用內(nèi)核所做的工作。系統(tǒng)調(diào)用可以為用戶空間提供訪問硬件資源的統(tǒng)一接口,以至于應(yīng)用程序不必去關(guān)注具體的硬件訪問操作。系統(tǒng)調(diào)用可以對(duì)系統(tǒng)進(jìn)行保護(hù),保證系統(tǒng)的穩(wěn)定和安全。系統(tǒng)調(diào)用的存在規(guī)定了用戶進(jìn)程進(jìn)入內(nèi)核的具體方式,用戶是不能從任意位置進(jìn)入內(nèi)核空間的,這保證了系統(tǒng)的穩(wěn)定性。
Linux中是以0x80號(hào)軟中斷引發(fā)的系統(tǒng)調(diào)用的,即0x80號(hào)中斷的處理程序就是位于kernel/entry_32.S中system_call函數(shù)。這個(gè)函數(shù)使用匯編實(shí)現(xiàn)的,它首先是保存現(xiàn)場(chǎng),然后通過eax查找對(duì)應(yīng)哪一個(gè)系統(tǒng)調(diào)用,然后查找sys_call_table調(diào)用對(duì)應(yīng)的系統(tǒng)調(diào)用。等完成調(diào)用之后檢查是否有信號(hào)發(fā)生或者需要進(jìn)程調(diào)度,如果有就進(jìn)入相應(yīng)的處理程序,并進(jìn)行進(jìn)程調(diào)度,如果沒有就恢復(fù)現(xiàn)場(chǎng)完成了整個(gè)系統(tǒng)調(diào)用,