天天看點

作業系統實驗02——添加系統調用(實驗)

一、系統調用過程(以printf實作為例):

  1. C函數庫将printf(“Hello World”)變成 write(1, buf, 11);
  2. 接下來是真正系統調用的過程,write展開成一段包含int 0x80的代碼,宏展開。主要完成
int 0x80
mov __NR_write, %eax
mov fd, %ebx
mov buff, %ecx
mov count, %edx
           

eax存放系統調用号, ebx, ecx, edx存放攜帶的參數

  1. 解釋執行int 0x80, 通過查找IDT表中的0x80對應表項,跳到system_call函數去執行
  2. system_call主要完成儲存使用者态的段寄存器,調用sys_call_table中的某個函數(sys_call_table是某一個函數表的起始位址),這一步會在sys_call_table中找到真正實作write功能的核心函數sys_write。
    作業系統實驗02——添加系統調用(實驗)
作業系統實驗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,也即代表此時進入核心态!

作業系統實驗02——添加系統調用(實驗)

通過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進入中斷,通過該中斷跳轉到中斷處理程式,進而進入核心,找到對應的核心實作函數。

繼續閱讀