系统调用介绍
- 系统调用原理
系统中的程序类型及状态
操作系统中的状态分为管态(核心态)和目态(用户态)。特权指令:一类只能在核心态下运行而不能在用户态下运行的特殊指令。不同的操作系统特权指令会有所差异,但是一般来说主要是和硬件相关的一些指令。访管指令:本身是一条特殊的指令,但不是特权指令。(trap指令)。基本功能:“自愿进管”,能引起访管异常。
用户程序只在用户态下运行,有时需要访问系统核心功能,这时通过系统调用接口使用系统调用。 - 系统功能调用
系统功能调用:就是用户在程序中使用“访管指令”调用由操作系统提供的子功能集合。其中每一个系统子功能称为一个系统调用命令,也叫广义指令。 -
系统调用的核心
用户程序包含一段int 0x80的代码(通常由库函数提供)
操作系统写中断处理获取想调程序的编号
操作系统根据编号执行相应代码
系统调用源码
理解系统调用重点在于理解int 80中断和中断向量表。
这里以open系统调用来解释系统调用的源码。
- 发起int 80中断
open系统调用的源码在lib/open.c 文件中,源码如下:
/*
* linux/lib/open.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__
#include <unistd.h>
#include <stdarg.h>
int open(const char * filename, int flag, ...)
{
register int res;
va_list arg;
va_start(arg,flag);
////c语言调用汇编,第一个参数是返回值,第二个参数是响应函数在系统调用表中的位置,第三个参数为文件名,第四个参数是打开的mode,
// %0 - eax(返回的描述符或出错码);%1 - eax(系统中断调用功能号__NR_open);
// %2 - ebx(文件名 filename);%3 - ecx(打开文件标志 flag);%4 - edx(后随参数文件属性 mode)
__asm__("int $0x80" //系统中断号,这个中断号表示系统调用
:"=a" (res)
:"0" (__NR_open),"b" (filename),"c" (flag),
"d" (va_arg(arg,int)));
if (res>=0)
return res;
errno = -res;
return -1;
}
查看0x80中断定义
0x80中断在kernel/sched.c中定义,代码如下:
void sched_init(void)
{
..........省略不相关代码
set_system_gate(0x80,&system_call); ///这里定义了0x80中断的入口(系统门)
}
set_system_gate 是个宏,在 include/asm/system.h 中定义为:
可以看出来0x80 是中断描述符中的下标号15 是中断类型,3是运行特权级,addr是中断入口函数
#define set_system_gate(n,addr) \
_set_gate(&idt[n],15,3,addr)
接下来看 system_call。该函数纯汇编打造,定义在 kernel/system_call.s 中:
nr_system_calls = 72 ///定义当前Linux有多少系统调用,其实就是include/linux/sys.h 中sys_call_table数组的长度
- 查找中断向量表
__NR_open 定义在unistd.h中
define __NR_open 5
显然,sys_call_table 一定是一个函数指针数组的起始地址,它定义在 include/linux/sys.h 中:
这里只挑选跟open系统调用有关的代码:
extern int sys_open();
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid };
可以看到sys_open 在数组下标5的位置
linux-0.11\fs\open.c 实现了文件系统的sys_open
添加自己的系统调用
案例介绍
- 在include/unistd.h 添加自定义系统调用宏
__NR_whoami 和 __NR_iam
#define __NR_whoami 72
#define __NR_iam 73
- 修改kernel/system_call.s
nr_system_calls = 74 ///原始值为72,我们添加两个系统调用,需要将数组改为74
- 修改include/linux/sys.h
添加如下代码
extern int sys_whoami();
extern int sys_iam();
将数组修改为
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid,sys_whoami, sys_iam};
- 模仿open.c 编写kernel/who.c
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <utime.h>
#include <sys/stat.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#define MAXLEN 23
struct {
char name[MAXLEN ];
int len;
} myName;
int sys_whoami(char *name,int len){
if(myName.len> len) return -1;
for(int i=0;i<myName.len;i++){
put_fs_byte( myName.name[i],name++);
}
return 0;
}
int sys_iam(char* name){
for(int i =0;i<MAXLEN ;i++){
char ch = get_fs_byte(name);
if(ch=='\0') break;
myName.name[i] = ch ;
myName.len++;
}
return 0;
}
- 修改makefile
[root@localhost linux-0.11]# vi 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
修改为:
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:
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
修改为:
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
- 编写用户程序 testiam.c
#define __LIBRARY__
#include <unistd.h>
_syscall1(int,iam,char *,name)
int main(int arg1,char* arg[]){
const char * a=arg[1];
char aa[40];
int ii;
printf("string:%s\n",a);
ii=iam(a);
return(0);
}
- 编写用户程序 testwhoami.c
#define __LIBRARY__
#include <unistd.h>
_syscall2(int,whoami,char *,name,unsigned int, size)
int main(int arg1,char* arg[]){
char aa[40];
whoami(aa,40);
printf("whoami=%s\n",aa);
return(0);
}
编译运行后效果如下: