操作系统(二)系统调用
在准备学习李治军老师的操作系统的时候,实验二的系统调用实验网上虽然有很多,但是有不少存在错误、步骤比较跳的的帖子,以及绝大多数都是实验过程中夹杂着很多理论,本文主要是将过程整合起来,没有过多的理论,以便同学们直接对着步骤完成实验再与理论结合起来。
另外值得一提的是,实验开始前将文件恢复初始化,特别是做过实验一的同学,需要将/boot和和tools中文件初始化,不然会有问题
# 删除原来的文件
cd path/oslab
sudo rm -rf ./*
# 重新解压确保代码初始化
tar -zxvf hit-oslab-linux-20110823.tar.gz
总得来说有以下添加和修改的文件
- 修改: include/unistd.h; include/linux/sys.h /kernel/system_call.s; kernel/Makefile
- 添加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

#define __NR_whiami 72
#define __NR_iam 73
修改sys.h

extern int sys_whoami();
extern int sys_iam();
# 注意顺序要相同
...,sys_whoami,sys_iam };
修改system_call.s

nr_system_calls = 74
修改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
添加以下文件
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

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

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

作于2024年09月08日