一、系統調用過程(以printf實作為例):
- C函數庫将printf(“Hello World”)變成 write(1, buf, 11);
- 接下來是真正系統調用的過程,write展開成一段包含int 0x80的代碼,宏展開。主要完成
int 0x80
mov __NR_write, %eax
mov fd, %ebx
mov buff, %ecx
mov count, %edx
eax存放系統調用号, ebx, ecx, edx存放攜帶的參數
- 解釋執行int 0x80, 通過查找IDT表中的0x80對應表項,跳到system_call函數去執行
- system_call主要完成儲存使用者态的段寄存器,調用sys_call_table中的某個函數(sys_call_table是某一個函數表的起始位址),這一步會在sys_call_table中找到真正實作write功能的核心函數sys_write。
作業系統實驗02——添加系統調用(實驗)
二、實驗步驟:
1 include/unistd.h 中添加新增的兩個宏 __NR_iam, __NR_iam
2 kernel/system_call.s 中将nr_system_calls(表示系統調用總數)改為74(新增兩個,原來72)
3 在include/linux/sys.h中對sys_call_table進行添加sys_iam, sys_whoami 同時在檔案中加上extern int sys_whoami(); extern int sys_iam();
注意:第一步的宏添加需要先在oslab下要通過mount方法完成對linux0.11中檔案的編輯
sudo ./mount-hdc
這樣再hdc檔案夾下會生成對應的linux0.11的檔案系統,進而可以修改其中檔案亦或是實作檔案傳遞。我們需要進入hdc/usr/include/unistd.h内添加宏(這樣才行,不然不能正常編譯連結)
當修改完成後記得解除安裝
sudo ./umount hdc
添加 printk() 調試who.c的時候,注意printk列印字元串時,直接printk(buf)即可,無需printk("%s")!!!
三、回顧整個過程
接下來我們分析我們最終完成的iam.c檔案的執行
// iam.c
#include<errno.h>
#define __LIBRARY__
#include<unistd.h>
#include<stdio.h>
_syscall1(int, iam, const char*, name);
int main(int argc, char **argv)
{
int num = iam(argv[1]);
printf("%d\n", num);
return 0;
}
首先,會對_syscall1(int, iam, const char*, name);在#include<unistd.h>中完成宏展開
// 展開後對内嵌彙編翻譯,大緻意思如下
int 0x80
mov %eax, __NR__iam
mov %ebx, name
解釋執行int 0x80, 通過查找IDT表中的0x80對應表項,跳到system_call函數去執行。
注意:通過IDT表我們找到的對應CS,此時的CS段選擇子,最後兩位是0,也即代表此時進入核心态!
通過call _sys_call_table(, %eax, 4) )(%eax即__NR__iam即72) 找到我們真正需要調用的核心函數(sys_call_table是某一個函數表的起始位址),我們之前已經完成:
#define __NR__iam 72,
以及添加sys_iam到sys_call_table中
是以此時sys_call_table(, 4*_NR__iam), 則找到對應的sys iam()核心函數,真正執行iam的功能,sys_iam()也是我們自己實作好的
#include<string.h>
#include<errno.h>
#include<asm/segment.h>
char msg[23]; //storage area
// we assume '\0' is also a byte
int sys_iam(const char* name)
{
char temp[30];
char *p = name;
int i;
for (i = 0; i < 30; i ++)
{
temp[i] = get_fs_byte(name+i);
if (temp[i] == '\0') break;
}
int len = i + 1;
if (len > 23) return -1;
strcpy(msg, temp);
return len;
}
int sys_whoami(char* name, unsigned int size)
{
char temp[30];
int i;
for (i = 0; i < 30; i ++)
{
temp[i] = msg[i];
if (temp[i] == '\0') break;
}
int len = i + 1;
if (len > size) return -1;
for (i = 0; i < len; i ++)
{
put_fs_byte(temp[i], name+i);
}
return len;
}
注意:get_fs_byte()是核心從使用者态擷取字元,put_fs_byte()則是将核心字元傳輸到使用者态。這兩個函數幫助我們在使用者态和核心态之間傳遞資料。
這樣,我們就完成了從使用者态iam.c對iam()系統調用的全過程啦!
其實關鍵就是Int 0x80進入中斷,通過該中斷跳轉到中斷處理程式,進而進入核心,找到對應的核心實作函數。