文章目录
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)
{
系统调用实践

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

被折叠的 条评论
为什么被折叠?



