Linux0.11操作系统(哈工大李治军老师)实验楼实验3-系统调用

这次内容是在Linux0.11上添加两个系统调用,并编写两个简单的应用程序测试它们

  1. iam
    第一个系统调用是 iam(),其原型为:
int iam(const char * name);

完成的功能是将字符串参数 name 的内容拷贝到内核中保存下来。要求 name 的长度不能超过 23 个字符。返回值是拷贝的字符数。如果 name 的字符个数超过了 23,则返回 “-1”,并置 errno 为 EINVAL。

在 kernal/who.c 中实现此系统调用。

  1. whoami
    第二个系统调用是 whoami(),其原型为:
int whoami(char* name, unsigned int size);

它将内核中由 iam() 保存的名字拷贝到 name 指向的用户地址空间中,同时确保不会对 name 越界访存(name 的大小由 size 说明)。返回值是拷贝的字符数。如果 size 小于需要的空间,则返回“-1”,并置 errno 为 EINVAL。

也是在 kernal/who.c 中实现。

  1. 测试程序
    运行添加过新系统调用的 Linux 0.11,在其环境下编写两个测试程序 iam.c 和 whoami.c。

调用系统调用,是调用系统库中为该系统调用编写的一个接口函数,叫 API(Application Programming Interface)。API 并不能完成系统调用的真正功能,它要做的是去调用真正的系统调用,过程是:

把系统调用的编号存入 EAX;
把函数参数存入其它通用寄存器;
触发 0x80 号中断(int 0x80)。

查看一下/lib/close.c,研究一下close() 的API:

#define __LIBRARY__
#include <unistd.h>

_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
/*
所以添加系统调用时需要修改include/unistd.h文件,
使其包含__NR_whoami和__NR_iam。
*/

_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,%%ax\n\t" \
	"movw %0,%%dx\n\t" \
	"movl %%eax,%1\n\t" \
	"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 <asm/segment.h>
#include <errno.h>
#include <string.h>

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++] != '\0');

    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("%s\n", _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

/* iam.c */
#define __LIBRARY__
#include <unistd.h> 
#include <errno.h>
#include <asm/segment.h> 
#include <linux/kernel.h>
_syscall1(int, iam, const char*, name);
   
int main(int argc, char *argv[])
{
    /*调用系统调用iam()*/
    iam(argv[1]);
    return 0;
}

/* whoami.c */
#define __LIBRARY__
#include <unistd.h> 
#include <errno.h>
#include <asm/segment.h> 
#include <linux/kernel.h>
#include <stdio.h>
   
_syscall2(int, whoami,char *,name,unsigned int,size);
   
int main(int argc, char *argv[])
{
    char username[64] = {0};
    /*调用系统调用whoami()*/
    whoami(username, 24);
    printf("%s\n", username);
    return 0;
}

/*
在应用程序中,要有:
*/

/* 有它,_syscall1 等才有效。详见unistd.h */
#define __LIBRARY__

/* 有它,编译器才能获知自定义的系统调用的编号 */
#include "unistd.h"

/* iam()在用户空间的接口函数 */
_syscall1(int, iam, const char*, name);

/* whoami()在用户空间的接口函数 */
_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 编译执行测试程序

在这里插入图片描述
到这里就算是完成了该实验

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值