第一个系统调用是iam(),其原型为:
int iam(const char * name);
完成的功能是将字符串参数name的内容拷贝到内核中保存下来。要求name的长度不能超过23个字符。返回值是拷贝的字符数。如果name的字符个数超过了23,则返回“-1”,并置errno为EINVAL。
在kernal/who.c中实现此系统调用。
第二个系统调用是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
_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
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
#define __LIBRARY__
#include
_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));
}
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上了,但是老是登不上,麻烦)
#########################################################################
# 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
/*
* Compile: "gcc testlab2.c"
* Run: "./a.out"
*/
#include
#include
#include
#include
#include
#define __LIBRARY__
#include
_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
#include
#include
_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
#include
_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;
}
#
# 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
仓库地址
跟实验一的代码放一块了