操作系统实验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——添加系统调用(实验)_第1张图片

操作系统实验02——添加系统调用(实验)_第2张图片

二、实验步骤:

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
#define __LIBRARY__
#include
#include

_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中完成宏展开

// 展开后对内嵌汇编翻译,大致意思如下
int 	0x80
mov	%eax, __NR__iam
mov %ebx, name

解释执行int 0x80, 通过查找IDT表中的0x80对应表项,跳到system_call函数去执行。
注意:通过IDT表我们找到的对应CS,此时的CS段选择子,最后两位是0,也即代表此时进入内核态!

操作系统实验02——添加系统调用(实验)_第3张图片

通过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
#include
#include

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进入中断,通过该中断跳转到中断处理程序,从而进入内核,找到对应的内核实现函数。

你可能感兴趣的:(操作系统实验)