一、什么是系统调用
-
接口:连接两个东西、信号转换、屏蔽细节……
-
图形界面:消息框架程序+消息处理程序。
-
命令:一个用c语言写的程序
-
操作系统接口:接口表示为函数调用,又由系统提供,所以称为系统调用。
-
POSIX:
分类 POSIX定义 含义 任务管理 fork 创建一个进程 excel 运行一个可执行程序 pthread_create 创建一个线程 文件系统 open 打开一个文件或目录 EACCES 返回值,表示没有权限 mode_t st_mode 文件头结构:文件属性
二、应用程序如何调用系统调用
在通常情况下,调用系统调用和调用一个普通的自定义函数在代码上并没有什么区别,到那时调用之后发生的事情有很大的不同。
- 调用自定义函数是通过call指令直接跳转到该函数的地址,继续运行。
- 而调用系统调用,是调用系统库中为该系统调用编写的一个接口函数,叫
API(Application Programming Interface)
。API并不能完成系统调用的真正功能,它要做的是去调用真正的系统调用,过程是:- 应用程序调用函数库(API);
- API将系统调用号存入
EAX
,然后通过中断调用使系统进入内核态; - 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
- 系统调用完成相应功能,将返回值存入
EAX
,返回到中断处理函数; - 中断处理函数返回到API中。
- API将
EAX
返回给应用程序。
三、实验报告
实验目的:
-
添加系统调用iam(),原型为:
int iam(const char * name);
-
第二个系统调用是whoami(),原型为:
int whoami(char* name, unsigned int size);
实验步骤:
-
既然我们知道了系统调用的原型,那么就先来编写这两个函数,再
~/work/oslab/linux-0.11/kernel
下编写who.c
程序,实现这两个函数:#define __LIBRARY__ #include <unistd.h> #include <errno.h> #include <asm/segment.h> #include <linux/kernel.h> char string_buf[64]={0}; int sys_iam(const char* name) { printk("debug:iam is called successfully\n"); int i =0; char char_buf; while((char_buf=get_fs_byte(name+i)) != '\0' && i<63) { string_buf[i]=char_buf; i++; } string_buf[i]='\0'; if(i>23) { errno=EINVAL; return -1; } else { return i; } } int sys_whoami(char* name,unsigned int size) { int i=0; char char_buf; while(i < 23) { char_buf=string_buf[i]; put_fs_byte(char_buf,name+i); i++; } put_fs_byte("\0",name+i); if(string_buf[i] != '\0') { errno = EINVAL; return -1; } else { return i; } }
-
我们已经知道进系统调用时,应用程序调用的是系统函数库,所以我们需要编写关于这两个函数 的系统API。在0.11的lib目录下,已经有一些实现的API,我们可以仿照的创建
iam.c
:#define __LIBRARY__ #include <unistd.h> _syscall1(int,iam,const char*,name);
再创建
whoami.c
:#define __LIBRARY__ #include <unistd.h> _syscall2(int,whoami,char*,name,unsigned int,size);
-
添加系统调用时需要修改
include/unistd.h
文件,使其包含__NR_whoami
和__NR_iam
。修改unistd.h文件,第一处修改是添加两行:define __NR_setregid 71 define __NR_iam 72 define __NR_whoami 73
第二处修改是文件的最后:
pid_t getpgrp(void); pid_t setsid(void); int iam(const char* name); int whoami(char* name,unsigned int size); #endif
-
然后再修改
~/work/oslab/linux-0.11/include/linux/sys.h
,添加:extern int sys_iam(); extern int sys_whoami();
在文件的最后添加:
sys_setregid,sys_iam,sys_whoami
-
由于我们添加了系统调用,所以必须修改
kernel/system_call.s
,其中第61行中,将72改为74,代表系统调用总数。nr_system_calls = 74
-
修改``kernal/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 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
-
在应用程序中,必须要有:
#define __LIBRARY__ /* 有它,_syscall1等才有效。详见unistd.h */ #include <unistd.h> /* 有它,编译器才能获知自定义的系统调用的编号 */ _syscall1(int, iam, const char*, name); /* iam()在用户空间的接口函数 */ _syscall2(int, whoami,char*,name,unsigned int,size); /* whoami()在用户空间的接口函数 */
-
编写应用程序:
-
user_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(argv[1]); return 0; }
-
user_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(username, 24); printf("%s\n", username); return 0; }
-
-
编写测试脚本
#!/bin/bash gcc use_iam.c -o iam gcc use_whoami.c -o whoami ./iam Lizhijun ./whoami
-
注意:一定要将
use_iam.c、use_whoami.c、和test.sh上传到linux0.11文件系统的同一文件夹下,然后执行。
注意:可能会报错如下:
原因是找不到系统调用好,但是明明改了的,这时需要在当前的Linux 0.11系统中,进入/usr/include
,修改unistd.h
,添加这两个系统调用。
运行: