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

系统调用实践
本文介绍了一个简单的系统调用实现案例,包括iam()和whoami()两个函数的设计与测试过程。通过对实验步骤、常见错误及解决方案的详细解析,帮助读者了解系统调用的基本原理。

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)
{
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值