【HIT-OSLAB-2-系统调用的实现】

lab2-system call

实验目的

  • 建立对系统调用接口的深入认识
  • 掌握系统调用的基本过程
  • 能完成系统调用的全面控制
  • 为后续实验做准备

实验内容

指导书给的内容

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中实现。

简单点

两个函数

在kernel/who.c中一块实现。

iam存储一个字符串

whoami将这个字符串复制到给定的字符指针所指向的区域。

如果长度不够,就返回-1,置errno为EINVAL。

实验中的记录

/lib/_who.c
#define __LIBRARY__
#include <unistd.h>
_syscall1(int, iam, const char*, name);
_syscall2(int, whoami, char*, name, unsigned int, size);
/include/unistd.h

#define __NR_whoami 72
#define __NR_iam 73
/include/linux/sys.h
extern int sys_whoami();
extern int sys_iam();

fn_ptr sys_call_table [] = {
    ...
    sys_whoami, sys_iam
};
/kernel/system_call.s
nr_system_calls = 74
/kernel/who.c
    #include <linux/kernel.h>
int sys_iam(const char* name) {
    return -1;
}

int sys_whoami(char *name, unsigned int size) {
    return -1;
}
/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 who.o
    
### Dependencies:
who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.h \
    ../include/errno.h
gcc -o iam iam.c -Wall
./iam
sync //写入磁盘

第一次报错信息

iam.c
    
#include <stdio.h>
#define __LIBRARY__
#include <unistd.h>
    
_syscall1(int, iam, const char*, name);
_syscall2(int, whoami, char*, name, unsigned int, size);

int main () {
    printf("%d\n", iam("rebel over waist"));
    printf("%d\n", whoami("who are you", 0));
    return 0;
}
__NR_whoami undeclared
__NR_iam undeclared

在unistd.h中存在函数调用
in function iam:
__NR_iam undeclared

in function whoami:
__NR_whoami undeclared

原因
在写好的库中

嗯
也就是在编译完运行之后的系统库中
确实不存在这两个号
我记得之前提到过

在0.11环境下编译C程序,包含的头文件都在/usr/include目录下。该目录下的unistd.h是标准头文件(它和0.11源码树中的unistd.h并不是同一个文件,虽然内容可能相同),没有__NR_whoami和__NR_iam两个宏,需要手工加上它们,也可以直接从修改过的0.11源码树中拷贝新的unistd.h过来。


怎么传值

又遇到了一个问题
就是参数并没有传进去
刚刚查看了汇编的源代码
确实是传了的
但是不知道为什么是那样

我觉得是因为指针传递的问题

也就是内核的数据和用户的数据其实并不是共享的

你所传递的字符串是用户区域的字符串的首地址

.globl _iam
_iam:
	pushl %ebp
	
	movl %esp, %ebp ; esp -> ebp
	subl $4, %esp ;alloc 4 bytes
	pushl %ebx ; push ebx 
	
	movl $73, %eax
	movl 8(%ebp), %ebx; get address of string
	int 0x80
	
	movl %eax, -4(%ebp)
	cmpl $0, -4(%ebp)
	jl L2
	movl -4(%ebp), %eax
	jmp L1
	.align 2
L2:
	movl -4(%ebp), %eax
	negl %eax
	movl %eax, _errno
	movl $-1, %eax
	jmp L1
	.align 2
L1:
	leal -8(%ebp), %esp
	popl %ebx
	leave
	ret
	.align 2
system_call:
	cmpl $nr_system_calls-1,%eax    # 调用号如果超出范围的话就在eax中置-1并退出
	ja bad_sys_call
	push %ds                        # 保存原段寄存器值
	push %es
	push %fs
# 一个系统调用最多可带有3个参数,也可以不带参数。下面入栈的ebx、ecx和edx中放着系统
# 调用相应C语言函数的调用函数。这几个寄存器入栈的顺序是由GNU GCC规定的,
# ebx 中可存放第1个参数,ecx中存放第2个参数,edx中存放第3个参数。
# 系统调用语句可参见头文件include/unistd.h中的系统调用宏。
	pushl %edx
	pushl %ecx		# push %ebx,%ecx,%edx as parameters
	pushl %ebx		# to the system call
	movl $0x10,%edx		# set up ds,es to kernel space
	mov %dx,%ds
	mov %dx,%es
# fs指向局部数据段(局部描述符表中数据段描述符),即指向执行本次系统调用的用户程序的数据段。
# 注意,在Linux 0.11 中内核给任务分配的代码和数据内存段是重叠的,他们的段基址和段限长相同。
	movl $0x17,%edx		# fs points to local data space
	mov %dx,%fs
# 下面这句操作数的含义是:调用地址=[_sys_call_table + %eax * 4]
# sys_call_table[]是一个指针数组,定义在include/linux/sys.h中,该指针数组中设置了所有72
# 个系统调用C处理函数地址。
	call sys_call_table(,%eax,4)        # 间接调用指定功能C函数

所以fs是关键

include/asm/segment.h
static inline unsigned char get_fs_byte(const char * addr)
{
	unsigned register char _v;

	__asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr));
	return _v;
}

static inline void put_fs_byte(char val,char *addr)
{
__asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr));
}

第二次错误 segment fault

length: 16
general protection: 0000
fs: 0010
base: 1000 0000
limit: 0400 0000
Segmentation fault

之前的代码使用的是指针版本

现在的代码使用的是数组版本

可能是因为char * p = const char* s

不被允许,p被修改了0?

第三个错误

代码自己测完全没问题

但是给测试程序,怎么也跑不对。

请添加图片描述

请添加图片描述

请添加图片描述

而且很奇怪,内核函数的错误输出在前面,但是测试样例在后面。

我还以为是我看错了,又特地加上了字符串长度,结果还是这样的。

但是我看了看程序的源码,都是正常的啊。

很正常的顺序执行,不就是调用了一下函数,和应该有的结果做了一下比较。

而且这个报错还真的就是返回值为-1的时候才有的报错。

晕了。

到网上找到了一份说是全通过的代码,但是看了一眼它的具体实现,我的天,sys_iam, sys_whoami连errno都不带,返回值不是-1,返回的是-EINVAL, 但是测试程序和我的一样,这是咋全对的?

又看了他的iam.c 和whoami.c,额,直接根据返回值给errno赋值。啧啧啧。

不想管了。

第四个问题

我在linux0.11是root用户

但是怎么也修改不了iam.c这种文件的删除,重命名权限。

总是说不是文件所有者

但是我用ls -al看了一下,所有者都是root啊

搞不懂

实验原理

系统调用的实现首先得理解下面这些步骤

应用程序调用库函数(API);
API将系统调用号存入EAX,然后通过中断调用使系统进入内核态;
内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
系统调用完成相应功能,将返回值存入EAX,返回到中断处理函数;
中断处理函数返回到API中;
API将EAX返回给应用程序。

调用API

为了方便去做系统调用和接口,linux采取了统一的系统调用接口,这些调用区分是参数个数和调用号。

参数最多3个。放到ebx ecx edx中。

调用号放到eax中。

调用号在unistd.h中。(注意,这里(include/unistd.h)的unistd.h和你跑的linux0.11中的unistd.h不是一个,也就是说你就算在include/unistd.h中修改了,在linux0.11中的那个也还是原来的样子。而你的应用程序,实际上include的是系统中的。所以你得找到这个库文件,然后给它加上几个系统调用号。这样才能在代码中调用成功。

系统调用总数在system_call.s中,由于你要新加系统调用,就得改一下总数。

system_call.s中的代码其实不需要修改,这是一个统一的接口,用来进行进程切换等等操作。

最后会根据include/linux/sys.h中的函数表找到对应功能的函数。

所以你这个时候得在这个表sys_call_table的指定位置,也就是你的系统调用号对应的位置,填上你要执行的函数。

这个函数并不在这头文件中实现。

所以你得在前面说明这是一个外部函数,然后找到去进行具体的实现。

实现就蛮简单了。完成指定的功能即可。

注意,包括一些用到的头文件。

比如printk,errno,EINVAL,get_fs_byte.

附记

如果你在实验过程中感到很苦恼,不知道什么该写什么,那就去看看linux中哪些系统调用是如何实现的?

在此举出几个有帮助的。

open, 和文件相关,涉及到了get_fs_byte

close 简单 明确 知道怎么写入口

syst.c 头文件该包含哪些

以及一些起到辅助作用的代码,用了老久时间琢磨,找了一堆方法,最后搞定的。

(整体项目扔github上了,但是老是登不上,麻烦)

将测试脚本 挂载到linux0.11中(在files文件夹中)

#########################################################################
# File Name:    syscall.sh
# Author:       rebelOverWaist
#########################################################################
#!/bin/bash
make clean && make all
cd ..
sudo ./mount-hdc
cd linux-0.11
cp iam.c whoami.c ../hdc/usr/root
cp testlab2.c testlab2.sh ../hdc/usr/root
cd ..
sudo umount hdc
./run

测试程序(防止没有files)

/*
 * Compile: "gcc testlab2.c"
 * Run: "./a.out"
 */

#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#define __LIBRARY__
#include <unistd.h>

_syscall2(int, whoami,char*,name,unsigned int,size);
_syscall1(int, iam, const char*, name);

#define MAX_NAME_LEN        23
#define NAMEBUF_SIZE        (MAX_NAME_LEN + 1)
/* truncate a long name to SHORT_NAME_LEN for display */
#define SHORT_NAME_LEN      (MAX_NAME_LEN + 2)

/*           name               score */
#define TEST_CASE { \
    {"x",                           10,  1, NAMEBUF_SIZE,  1},\
    {"sunner",                      10,  6, NAMEBUF_SIZE,  6},\
    {"Twenty-three characters",      5, 23, NAMEBUF_SIZE, 23},\
    {"123456789009876543211234",     5, -1, 0,            -1},\
    {"abcdefghijklmnopqrstuvwxyz",   5, -1, 0,            -1},\
    {"Linus Torvalds",               5, 14, NAMEBUF_SIZE, 14},\
    {"",                             5,  0, NAMEBUF_SIZE,  0},\
    {"whoami(0xbalabala, 10)",       5, 22,           10, -1},\
    {NULL, 0, 0, 0, 0}  /* End of cases */ \
}
/*改动一:增加size,和rval2*/

int test(const char* name, int max_score, int expected_rval1, int size, int expected_rval2);
void print_message(const char* msgfmt, const char* name);

struct test_case 
{
    char *name;
    int score;
    int rval1;  /* return value of iam() */
     /*改动2:增加size,和rval2定义*/
    int size;   /*Patch for whoami,2009.11.2*/
    int rval2;  /* return value of whoami() */
};

int main(void)
{
    struct test_case cases[] = TEST_CASE;

    int total_score=0, i=0;

    while (cases[i].score != 0)
    {
        int score;

        printf("Test case %d:", i+1);

         /*改动3:增加size,和rval2的参数阿*/
        score = test( cases[i].name, 
                      cases[i].score, 
                      cases[i].rval1,
                      cases[i].size,
                      cases[i].rval2 );

        total_score += score;
        i++;
    }

    printf("Final result: %d%%\n", total_score);
    return 0;

}
 /*改动4:增加size,和rval2的声明*/
int test(const char* name, int max_score, int expected_rval1, int size, int expected_rval2)
{
    int rval;
    int len;
    char * gotname;
    int score=-1;

    assert(name != NULL);

    print_message("name = \"%s\", length = %d...", name);

    /*Test iam()*/
    len = strlen(name);
    rval = iam(name);
    /* printf("Return value = %d\n", rval);*/
 
/*改动5:增加的expected_rval1*/
    if (rval == expected_rval1)
    {
        if (rval == -1 && errno == EINVAL) /*The system call can detect bad name*/
        {
            /* print_message("Long name, %s(%d), detected.\n", name);*/
            printf("PASS\n");
            score = max_score;
        }
        else if (rval == -1 && errno != EINVAL)
        {
            printf("\nERROR iam(): Bad errno %d. It should be %d(EINVAL).\n", errno, EINVAL);
            score = 0;
        }
        /* iam() is good. Test whoami() next. */
    }
    else
    {
        printf("\nERROR iam(): Return value is %d. It should be %d.\n", rval, expected_rval1);
        score = 0;
    }

    if (score != -1) 
        return score;

    /*Test whoami()*/
    gotname = (char*)malloc(len+1);
    if (gotname == NULL)
        exit(-1);

    memset(gotname, 0, len+1);

    /* printf("Get: buffer length = %d.\n", len+1); */

    rval = whoami(gotname, size);
    /* printf("Return value = %d\n", rval); */

/*改动6:增加的expected_rval2*/
/*改动++:比较多 ,但还是顺序的改改*/

    if(rval == expected_rval2)
    {   
        if(rval == -1)
        {
            printf("PASS\n");
            score = max_score;
        }       
        else 
        {
            if (strcmp(gotname, name) == 0)
            {
                /* print_message("Great! We got %s(%d) finally!\n", gotname); */
                printf("PASS\n");
                score = max_score;
            }
            else
            {
                print_message("\nERROR whoami(): we got %s(%d). ", gotname);
                print_message("It should be %s(%d).\n", name);
                score = 0;
            }
        }
    }
    else if (rval == -1)
    {
        printf("\nERROR whoami(): Return value is -1 and errno is %d. Why?\n", errno);
        score = 0;
    }
    else 
    {
        printf("\nERROR whoami(): Return value should be %d, not %d.\n", expected_rval2, rval);
        score = 0;
    }

    free(gotname);
    assert(score != -1);

    return score;
}

void print_message(const char* msgfmt, const char* name)
{
    char short_name[SHORT_NAME_LEN + 4] = {0};
    int len;
    
    len = strlen(name);

    if (len == 0)
    {
        strcpy(short_name, "NULL");
    }
    else if (len <= SHORT_NAME_LEN)
    {
        strcpy(short_name, name);
    }
    else
    {
        memset(short_name, '.', SHORT_NAME_LEN+3);
        memcpy(short_name, name, SHORT_NAME_LEN);
    }
    
    printf(msgfmt, short_name, len);
}

#/bin/sh

string1="Sunner"
string2="Richard Stallman"
string3="This is a very very long string!"

score1=10
score2=10
score3=10

expected1="Sunner"
expected2="Richard Stallman"
expected3="Richard Stallman"

echo Testing string:$string1
./iam "$string1"
result=`./whoami`
if [ "$result" = "$expected1" ]; then
	echo PASS.
else
	score1=0
	echo FAILED.
fi
score=$score1

echo Testing string:$string2
./iam "$string2"
result=`./whoami`
if [ "$result" = "$expected2" ]; then
	echo PASS.
else
	score2=0
	echo FAILED.
fi
score=$score+$score2

echo Testing string:$string3
./iam "$string3"
result=`./whoami`
echo "$result"
if [ "$result" = "$expected3" ]; then
	echo PASS.
else
	score3=0
	echo FAILED.
fi
score=$score+$score3

let "totalscore=$score"
echo Score: $score = $totalscore%

/* ************************************************************************
> File Name:     whoami.c
> Author:        rebelOverWaist
> Created Time:  2022年11月05日 星期六 18时30分35秒
> Description:  
 ************************************************************************/
#define __LIBRARY__
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

_syscall2(int, whoami, char*, name, unsigned int, size)
#define SIZE 23
int main (void) {
    char name[SIZE + 1];
    int res;
    res = whoami(name, SIZE+1);
    if(res == -1) {
        errno = EINVAL;
     } else {
        printf("%s\n", name);
     }
    return res;
}


/* ************************************************************************
> File Name:     iam.c
> Author:        rebelOverWaist
> Description:   
 ************************************************************************/
#define __LIBRARY__
#include <unistd.h>
#include <errno.h>

_syscall1(int, iam, const char*, name)

#define NAMELEN 100

char name[NAMELEN];

int main (int argc, char *argv[]) {
    int res;
    int namelen = 0;
    if(2 <= argc) {
        while((name[namelen] = argv[1][namelen]) != '\0')
            namelen++;
        printf("iam.c: %s, %d\n", name, namelen);
        res = iam(name);
        if(res == -1) 
            errno = EINVAL;
        return res;
      }
     return 0;
}

kernel中的makefile代码

#
# Makefile for the FREAX-kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#

AR	=ar
AS	=as --32
LD	=ld
LDFLAGS	=-m elf_i386 -x
CC	=gcc-3.4 -march=i386
CFLAGS	=-m32 -g -Wall -O -fstrength-reduce -fomit-frame-pointer \
	-finline-functions -nostdinc -I../include
CPP	=gcc-3.4 -E -nostdinc -I../include

.c.s:
	$(CC) $(CFLAGS) \
	-S -o $*.s $<
.s.o:
	$(AS) -o $*.o $<
.c.o:
	$(CC) $(CFLAGS) \
	-c -o $*.o $<

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

kernel.o: $(OBJS)
	$(LD) -m elf_i386 -r -o kernel.o $(OBJS)
	sync

clean:
	rm -f core *.o *.a tmp_make keyboard.s
	for i in *.c;do rm -f `basename $$i .c`.s;done
	(cd chr_drv; make clean)
	(cd blk_drv; make clean)
	(cd math; make clean)

dep:
	sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
	(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
		$(CPP) -M $$i;done) >> tmp_make
	cp tmp_make Makefile
	(cd chr_drv; make dep)
	(cd blk_drv; make dep)

### Dependencies:
who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.h \
	../include/errno.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
fork.s fork.o: fork.c ../include/errno.h ../include/linux/sched.h \
  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
  ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
  ../include/asm/segment.h ../include/asm/system.h
mktime.s mktime.o: mktime.c ../include/time.h
panic.s panic.o: panic.c ../include/linux/kernel.h ../include/linux/sched.h \
  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
  ../include/linux/mm.h ../include/signal.h
printk.s printk.o: printk.c ../include/stdarg.h ../include/stddef.h \
  ../include/linux/kernel.h
sched.s sched.o: sched.c ../include/linux/sched.h ../include/linux/head.h \
  ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
  ../include/signal.h ../include/linux/kernel.h ../include/linux/sys.h \
  ../include/linux/fdreg.h ../include/asm/system.h ../include/asm/io.h \
  ../include/asm/segment.h
signal.s signal.o: signal.c ../include/linux/sched.h ../include/linux/head.h \
  ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
  ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h
sys.s sys.o: sys.c ../include/errno.h ../include/linux/sched.h \
  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
  ../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \
  ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h \
  ../include/sys/times.h ../include/sys/utsname.h
traps.s traps.o: traps.c ../include/string.h ../include/linux/head.h \
  ../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \
  ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
  ../include/asm/system.h ../include/asm/segment.h ../include/asm/io.h
vsprintf.s vsprintf.o: vsprintf.c ../include/stdarg.h ../include/string.h

仓库地址
跟实验一的代码放一块了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值