- iam
第一个系统调用是 iam(),其原型为:
int iam(const char * name);
完成的功能是将字符串参数 name 的内容拷贝到内核中保存下来。要求 name 的长度不能超过 23 个字符。返回值是拷贝的字符数。如果 name 的字符个数超过了 23,则返回 “-1”,并置 errno 为 EINVAL。
在 kernal/who.c 中实现此系统调用。
- whoami
第二个系统调用是 whoami(),其原型为:
int whoami(char* name, unsigned int size);
它将内核中由 iam() 保存的名字拷贝到 name 指向的用户地址空间中,同时确保不会对 name 越界访存(name 的大小由 size 说明)。返回值是拷贝的字符数。如果 size 小于需要的空间,则返回“-1”,并置 errno 为 EINVAL。
也是在 kernal/who.c 中实现。
- 测试程序
运行添加过新系统调用的 Linux 0.11,在其环境下编写两个测试程序 iam.c 和 whoami.c。
调用系统调用,是调用系统库中为该系统调用编写的一个接口函数,叫 API(Application Programming Interface)。API 并不能完成系统调用的真正功能,它要做的是去调用真正的系统调用,过程是:
把系统调用的编号存入 EAX;
把函数参数存入其它通用寄存器;
触发 0x80 号中断(int 0x80)。
查看一下/lib/close.c,研究一下close() 的API:
#define __LIBRARY__ #include_syscall1(int, close, int, fd)
其中_syscall1是一个宏定义,在include/unistd.h中定义
#define _syscall1(type,name,atype,a)
type name(atype a)
{
long __res;
__asm__ volatile ("int $0x80"
: "=a" (__res)
: "0" (__NR_##name),"b" ((long)(a)));
if (__res >= 0)
return (type) __res;
errno = -__res;
return -1;
}
这里将__NR_close的调用号存入eax中,fd存入ebx中,然后执行 int $0x80中断调用。
int 0x80触发以后,就是内核的中断处理了。
调用IDT(中断描述表)中0x80对应位置的system_call。
__NR_close在include/unistd.h中定义:
#define __NR_close 6
_NR##name用于确定调用sys_call_table表中的哪一个中断处理函数,sys_call_table表定义在include/linux/sys.h
extern int sys_setup();
extern int sys_exit();
extern int sys_fork();
extern int sys_read();
extern int sys_write();
extern int sys_open();
extern int sys_close();
extern int sys_waitpid();
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid....}
而使用int 0x80来调用系统调用,实在操作系统初始化时,在
sched_init();
中完成了对系统调用的初始化。
sched_init()
{
set_system_gate(0x80,&system_call);
}
#define set_system_gate(n,addr)
_set_gate(&idt[n],15,3,addr)
#define _set_gate(gate_addr,type,dpl,addr)
__asm__ ("movw %%dx,%%axnt"
"movw %0,%%dxnt"
"movl %%eax,%1nt"
"movl %%edx,%2"
:
: "i" ((short) (0x8000+(dpl<<13)+(type<<8))),
"o" (*((char *) (gate_addr))),
"o" (*(4+(char *) (gate_addr))),
"d" ((char *) (addr)),"a" (0x00080000))
movw %%dx,%%ax 将ax置为0008:addr
movw %0,%%dx 将dx置为0x8000+(dpl<<13)+(type<<8),
分别赋给idt中0x80偏移的低4字节,高4字节。
所以当执行int 0x80中断时,就调用system_call
在本实验中,我们在/oslab/linux-0.11/include/unistd.h 中添加两个调用号
#define __NR_whoami 72 #define __NR_iam 73
并且将kernel/system_call.s中
nr_system_calls = 72 改为 nr_system_calls = 74
在include/linux/sys.h中添加系统调用名,并且更新系统调用表
extern int sys_whoami();
extern int sys_iam();
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid....sys_whoami, sys_iam}
接下来为新添加的系统调用编写实现代码
在linux-0.11/kernel目录下,创建who.c
#include #include#include char _myname[24]; int sys_iam(const char *name) { char str[25]; int i = 0; do { // get char from user input str[i] = get_fs_byte(name + i); } while (i <= 25 && str[i++] != ' '); if (i > 24) { errno = EINVAL; i = -1; } else { // copy from user mode to kernel mode strcpy(_myname, str); } return i; } int sys_whoami(char *name, unsigned int size) { int length = strlen(_myname); printk("%sn", _myname); if (size < length) { errno = EINVAL; length = -1; } else { int i = 0; for (i = 0; i < length; i++) { // copy from kernel mode to user mode put_fs_byte(_myname[i], name + i); } } return length; }
之后修改kernel/Makefile文件,用以链接kernel/who.c与其它代码
vim +27 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 who.o
### 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
再完成系统的编译,为linux-0.11编写测试程序iam.c, whoami.c
#define __LIBRARY__ #include#include #include #include _syscall1(int, iam, const char*, name); int main(int argc, char *argv[]) { iam(argv[1]); return 0; }
#define __LIBRARY__ #include#include #include #include #include _syscall2(int, whoami,char *,name,unsigned int,size); int main(int argc, char *argv[]) { char username[64] = {0}; whoami(username, 24); printf("%sn", username); return 0; }
#define __LIBRARY__ #include "unistd.h" _syscall1(int, iam, const char*, name); _syscall2(int, whoami,char*,name,unsigned int,size);
把编写好的测试程序通过挂载到虚拟机操作系统
~/oslab$ sudo ./mount-hdc ~/oslab$ cp iam.c whoami.c hdc/usr/root
执行脚本./run 进入虚拟机操作系统修改虚拟机系统调用号跟第一步是一样的
#define __NR_whoami 72 #define __NR_iam 73
在虚拟机 gcc 编译执行测试程序
到这里就算是完成了该实验



