操作系统实现系统调用的基本过程是:
在 Linux 0.11 上添加两个系统调用,并编写两个简单的应用程序测试它们。
linux-0.11/include/linux/sys.h
,把iam()
和whoami()
两个函数加入到全局变量和中断向量表中。中断被调用的时候,先查中断向量表,找到相应的函数名,调用其函数。//在原有的 extern 后面加上:
extern int sys_whoami();
extern int sys_iam();
//在sys_call_table[]中新增以下两个函数名:
sys_iam,sys_whoami
copy
linux-0.11/kernel/system_call.s
中的nr_system_calls
的72 改为 74,表明系统调用总数变成了 74 个nr_system_calls = 74
copy
unistd.h
。注意该文件不能直接在oslab/linux-0.11/include/
中修改,而要在虚拟机中修改,在oslab
目录下运行sudo ./mount-hdc
把虚拟机硬盘挂载在oslab/hdc
目录下,在hdc/usr/include
目录下修改unistd.h
。把两个自定义函数的宏定义在这里:#define __NR_iam 72
#define __NR_whoami 73
copy
修改 Makefile
Makefile
里记录的是所有源程序文件的编译、链接规则,要想让我们添加的kernel/who.c
可以和其他 linux 代码编译链接到一起,必须要修改 Makefile
文件。
打开kernel/Makefile
,将下面这段代码
OBJS = sched.o system_call.o traps.o asm.o fork.o \
panic.o printk.o vsprintf.o sys.o exit.o \
signal.o mktime.o
copy
改为:
OBJS = sched.o system_call.o traps.o asm.o fork.o \
panic.o printk.o vsprintf.o sys.o exit.o \
signal.o mktime.o who.o
copy
同时,还要将下面这段代码:
### Dependencies:
exit.s exit.o: exit.c ../include/errno.h ../include/signal.h \
../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
../include/asm/segment.h
copy
改为:
### Dependencies:
who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.h
exit.s exit.o: exit.c ../include/errno.h ../include/signal.h \
../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
../include/asm/segment.h
copy
Makefile 修改后,和往常一样 make all
就能自动把 who.c
加入到内核中了。
如果编译时提示 who.c
有错误,就说明修改生效了。所以,有意或无意地制造一两个错误也 不完全是坏事,至少能证明 Makefile 是对的。
编写who.c
在linux-0.11/kernel
下新建who.c
文件,编写下列内容:
/* 有它,_syscall1 等才有效。详见unistd.h */
#define __LIBRARY__
/* 有它,编译器才能获知自定义的系统调用的编号 */
#include "unistd.h"
#include <errno.h>
#include <asm/segment.h>
#include <string.h>
char username[64] = {0};
int sys_iam(const char *name)
{
int i = 0;
while (get_fs_byte(&name[i]) != '\0')
i++;
if (i > 23)
return -EINVAL;
printk("length of name is %d\n", i);
i = 0;
while ((username[i] = get_fs_byte(&name[i])) != '\0')
i++;
return i;
}
int sys_whoami(char *name, unsigned int size)
{
int len = strlen(username);
if (size < len)
{
return -1;
}
int i = 0;
while (i < len)
{
put_fs_byte(username[i], &name[i]);
i++;
}
return i;
}
copy
此处get_fs_byte()
与put_fs_byte()
均是按地址位读取,即指针操作,且-EINVAL
的含义是:无效的参数。用来指出传递给库函数参数时候的各种问题。
接下来编写iam.c
和whoami.c
两个文件,注意两个文件需要先mount-hdc
,然后放在hdc/usr/root
的目录下,这样就可以在启动bochs
后看到这两个文件。
iam.c
#define __LIBRARY__
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
/* iam()在用户空间的接口函数 */
_syscall1(int, iam, const char *, name);
int main(int argc, char *argv[])
{
if (argc <= 1)
{
printf("Input Exception!\n");
return -1;
}
if (iam(argv[1]) < 0)
{
printf("SystemCall Exception!\n");
return -1;
}
return 0;
}
copy
whoami.c
#define __LIBRARY__
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* whoami()在用户空间的接口函数 */
_syscall2(int, whoami, char *, name, unsigned int, size);
int main(int argc, char *argv[])
{
char *username = (char*) malloc(sizeof(char) * 128);
int counter = whoami(username, 128);
if (counter < 0)
{
printf("SystemCall Exception!");
return -1;
}
else
{
printf("%s\n", username);
}
free(username);
return 0;
}
copy
testlab2.sh
将脚本 testlab2.sh
(在 /home/teacher
目录下) 在修改过的 Linux 0.11 上运行,显示的结果即应用程序的得分。满分 30%
编译并测试:
gcc -o iam iam.c -Wall gcc -o whoami whoami.c -Wall ./testlab2.sh
copy
testlab2.c
将 testlab2.c
(在 /home/teacher
目录下) 在修改过的 Linux 0.11 上编译运行,显示的结果即内核程序的得分。满分 50%
(1)当应用程序经过库函数向内核发出一个中断调用。其中寄存器
eax
中存放着系统调用编号,参数可以依次存放在ebx,ecx,edx
中。因此从 Linux 0.11 现在的机制看,它的系统调用最多能传递3个参数。为了方便执行,内核源码在include/unistd
中定义了宏函数_syscall0()
,_syscall1()
,_syscall2()
,_syscall3()
分别表示传递0,1,2,3个参数。前两个参数是系统调用的名称,后面的参数是所携带参数的类型和名称。(2)若想扩大这个限制,传递多个参数,大块数据给内核,可以考虑传递这块数据的指针值。如系统调用:
typedef long off_t; int read(int fildes, char* buf, off_t count);
copy
其宏形式:
_syscall3(int, read, int, fildes, char*, buf, off_t, n)
copy
- 修改
include/unistd.h
,在其中加入新的系统调用宏定义:
#define __NR_foo 72 /*linux-0.11中共72个,从0-71,这里新增一个72*/
copy
- 修改
include/linux/sys.h
,在里面加上
extern int sys_foo();
copy
并在数组
fn_ptr sys_call_table[]
中加上一项sys_foo
。
- 修改
kernel/system_call.s
中的系统调用个数nr_system_calls = 73
,即新增一个。- 在
kernel
文件中编写实现新加的系统调用函数文件foo.c
- 修改
kernel/Makefile
,修改OBJS
和Dependencies
添加新的系统调用,使其在编译linux时编译新加的系统调用。
学习时间 668分钟
操作时间 39分钟
按键次数 221次
实验次数 28次
报告字数 5649字
是否完成 完成