鄭德倫 原創作品轉載請註明出處 《Linux內核分析》MOOC課程
http://mooc.study.163.com/course/USTC-1000029000
1. 向MenuOS系統中加入自己寫的系統調用函數:
1.1我們首先下載最新的MenuOS代碼,此代碼中將time加入了MenuOS中,而且修改了Makefile,輸入make rootfs即可製作根文件系統,使用qemu啓動內核加載rootfs.img文件系統。
我們輸入命令:
git clone https://github.com/mengning/menu.git
cd menu
make rootfs
即可完成環境的配置。
1.2然後我們打開test.c文件加入上次實驗寫的系統調用函數
加入兩個函數
int newdir(int argc, char *argv[])
{
if(mkdir("new dir", S_IRWXU) == 0)
printf("new dir create success\n");
else
printf("new dir create failed\n");
return 0;
}
int newdirAsm(int argc, char *argv[])
{
char *dirName = "new dirAsm";
mode_t mode = S_IRWXU;
int returnValue = 0;
asm
(
"movl $39, %%eax\r\n"
"movl %1, %%ebx\r\n"
"movl %2, %%ecx\r\n"
"int $0x80\r\n"
"movl %%eax, %0"
:"=m"(returnValue)
:"d"(dirName),"D"(mode)
);
if(returnValue == 0)
printf("new dirAsm create success\n");
else
printf("new dirAsm create failed\n");
return 0;
}
並且在main函數裏面加入
MenuConfig("newdir","create a new directory",newdir);
MenuConfig("newdir-asm","create a new directory(asm)",newdirAsm);
完成之後我們重新編譯,make rootfs編譯並且進入內核。
輸入newdir和newdir-asm就會發現命令生效了
2. 跟蹤中斷處理的代碼
我們使用gdb來調試跟蹤一下MenuOS中mkdir的系統調用過程。
首先在終端輸入
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s –S
並且在另一個終端打開gdb
輸入
gdbtui
(gdb)file linux-3.18.6/vmlinux #加載符號表
(gdb)target remote:1234 #建立gdb和gdbserver之間的連接
(gdb)b sys_mkdir #在sys_mkdir建立斷點
(gdb)c
然後我們在qemu中輸入newdir,會觸發斷點,然後我們進入gdb跟蹤調試
斷點停到SYSCALL_DEFINE2.
這個SYSCALL_DEFINE2在這裏其實就是
asmlinkage long sys_mkdir(const char __user *pathname, umode_t mode)
因爲:我們打開源碼中/linux-3.18.6/include/linux/syscalls.h文件,會看到一堆宏定義。
我們拿剛纔那個定義爲例:、
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
這是把SYSCALL_DEFINE2定義爲SYSCALL_DEFINEx然後把name與下劃線連接(##表示前後兩個參數當做串連接)
然後看下SYSCALL_DEFINEx的定義
#define SYSCALL_DEFINEx(x, sname, ...) \
SYSCALL_METADATA(sname, x, __VA_ARGS__) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
我們繼續找到__SYSCALL_DEFINEx的定義。
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
…
剩餘部分省略
__這裏把SYSCALL_DEFINEx定義爲asmlinkage long sys##name…
而name就是前面的_name,在這裏也就是_mkdir。
所以SYSCALL2_DEFINE2定義爲了asmlinkage long sys_mkdir,正式我們要找的系統調用。
弄清楚了之後,接下來我們繼續跟蹤。
我們在此處x/s pathname打印一下參數,看到參數正是我們要創建的文件夾名字
我們按s步入執行,發現進入了一個函數SYSCALL_DEFINE3,根據上面的理解,這裏應該是sys_mkdirat系統調用
我們來看下這個函數裏面的操作
user_path_create
security_path_mkdir
vfs_mkdir
done_path_create
我們步入執行進入user_path_create
裏面會調用kern_path_create
然後進入kern_path_create,裏面還會調用do_path_lookup
然後我們返回到SYSCALL_DEFINE3中執行完畢,會跳入到schedule中
如果繼續執行的話就會進入到不可調試的部分只能通過閱讀代碼來跟蹤了。
上面的調用過程我們來用一個流程圖來描述一下:
3. 分析系統調用過程的彙編代碼
/linux-3.18.6/arch/x86/kernel/entry_32.S中ENTRY(system_call)與ENDPROC(system_call)之間的內容就是完成系統調用的彙編代碼。
提取出主要的處理過程就是:
ENTRY(system_call)
SAVE_ALL
system_call:
call *sys_call_table(,%eax,4)#根據系統調用號來調用系統調用的處理函數
…
system_exit:
testl $_TIF_ALLWORK_MASK, %ecx #判斷當前任務是否需要處理syscall_exit_work
jne syscall_exit_work #如果需要處理則跳轉到syscall_exit_work
restore_all:
RESTORE_INT_REGS #不需要處理的話進行RESTORE_ALL
irq_return:
INTERRUPT_RETURN #返回
ENDPROC(system_call)
整個中斷處理的過程我們可以用一個流程圖畫出來