操作系统(二)系统调用

Jan 于 2024-09-08 发布 浏览量

操作系统(二)系统调用

在准备学习李治军老师的操作系统的时候,实验二的系统调用实验网上虽然有很多,但是有不少存在错误、步骤比较跳的的帖子,以及绝大多数都是实验过程中夹杂着很多理论,本文主要是将过程整合起来,没有过多的理论,以便同学们直接对着步骤完成实验再与理论结合起来。

另外值得一提的是,实验开始前将文件恢复初始化,特别是做过实验一的同学,需要将/boot和和tools中文件初始化,不然会有问题

# 删除原来的文件
cd path/oslab
sudo rm -rf ./*

# 重新解压确保代码初始化
tar -zxvf hit-oslab-linux-20110823.tar.gz 

总得来说有以下添加和修改的文件

  1. 修改: include/unistd.h; include/linux/sys.h /kernel/system_call.s; kernel/Makefile
  2. 添加3个文件: kernel/who.c; iam.c; whoami.c

实验内容

在 Linux 0.11 上添加两个系统调用,并编写两个简单的应用程序测试它们。

(1)iam() 第一个系统调用是 iam(),其原型为:

int iam(const char * name);

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

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

(2)whoami()
第二个系统调用是 whoami(),其原型为:

int whoami(char* name, unsigned int size);

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

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

(3)测试程序
运行添加过新系统调用的 Linux 0.11,在其环境下编写两个测试程序 iam.c 和 whoami.c。最终的运行结果是:

$ ./iam lizhijun

$ ./whoami

lizhijun

实验过程

修改unistd.h

image

#define __NR_whiami     72
#define __NR_iam        73

修改sys.h

image-20240908145800074

extern int sys_whoami();
extern int sys_iam();

# 注意顺序要相同
...,sys_whoami,sys_iam };

修改system_call.s

image-20240908145840867

nr_system_calls = 74

修改Makefile

image-20240908145641168

image-20240908145708004

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

添加以下文件

who.c

/* 功能:核心态文件,在who.c里实现系统调用sys_iam()与sys_whoami() */
    
#include<errno.h>         /* linux-0.11/include */
#include<unistd.h>
#include<asm/segment.h>  /* linux-0.11/include/asm */
#include<linux/kernel.h>
/* 直接包含的头文件是到linux-0.11/include目录 */

#define MAXLEN 23
char username[MAXLEN+1] = {0};        /* 内核态下,存储名字 */

int sys_iam(const char* myname){   /* myname为_syscallx调用时保存好了的 */

	printk("sys_iam run......\n");
	unsigned int i = 0;
	unsigned int namelen = 0;

	/* get_fs_byte(const char * addr) 从用户空间逻辑地址addr取出一个字节数据 */
	while(get_fs_byte(myname+namelen)!='\0')
		++namelen;                          
	if(namelen > MAXLEN){
		errno = EINVAL;    /* #define EINVAL  22 : Invalid argument */   
		return -EINVAL;
	}
	printk("namelen:%d\n",namelen);  /* 核心态,用printk() */
	while(i < namelen){
		username[i] = get_fs_byte(myname+i);  /* 将用户态下传递的字符串参数拷贝到内核中保存 */
		++i;
	}
	username[i] = '\0';
	printk("username: %s\n",username);
	return namelen;
}

int sys_whoami(char* myname, unsigned int size){

	printk("sys_whoami run......\n");
	unsigned int i = 0;
	unsigned int namelen = 0;
	while(username[namelen]!='\0')
		++namelen;
	if(size < namelen){   /* size小于所需的拷贝空间 */
		errno = EINVAL;
		return -EINVAL;
	}
	for(; i <= namelen; ++i){
		put_fs_byte(username[i], myname+i); /* 将内核态里的数据拷贝到用户态文件里 */
	}
	printk("namelen: %d\n",namelen);
	return namelen;
}

iam.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;
}

准备工作完成,准备编译

cd /path/oslab/linux-0.11
make clean
make all
#确保最后出现sync

image-20240908151816737

cd /path/oslab
./mount-dhc
cp iam.c whoiam.c hdc/usr/root
vi hdc/usr/include/unistd.h
#任意处添加这两条

#define __NR_whoami 72 
#define __NR_iam 73


cd /path/oslab
./run
#进入到Bochs
gcc -o whoiam whoiam.c
gcc -o iam iam.c
./iam jjt
./whoiam

image-20240908144304720

ps: 如果是个人本地环境搭建的环境下没有testlab2.sh,做不了最后的验证得分环节 可以将test2lab2.sh拷贝到hdc/usr/root下

test2lab.sh

#/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`
if [ "$result" = "$expected3" ]; then
	echo PASS.
else
	score3=0
	echo FAILED.
fi
score=$score+$score3

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

testlab2.c

/*
 * 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 */ \
}
/*\u6539\u52a8\u4e00\uff1a\u589e\u52a0size,\u548crval2*/

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() */
     /*\u6539\u52a82\uff1a\u589e\u52a0size,\u548crval2\u5b9a\u4e49*/
    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);

         /*\u6539\u52a83\uff1a\u589e\u52a0size,\u548crval2\u7684\u53c2\u6570\u963f*/
        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;

}
 /*\u6539\u52a84\uff1a\u589e\u52a0size,\u548crval2\u7684\u58f0\u660e*/
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);*/
 
/*\u6539\u52a85\uff1a\u589e\u52a0\u7684expected_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); */

/*\u6539\u52a86\uff1a\u589e\u52a0\u7684expected_rval2*/
/*\u6539\u52a8\uff0b\uff0b\uff1a\u6bd4\u8f83\u591a \uff0c\u4f46\u8fd8\u662f\u987a\u5e8f\u7684\u6539\u6539*/

    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);
}


chmod +x testlab2.sh
./testlab2.sh

image

作于2024年09月08日