系统调用
系统调用的过程
void sched_init(void)
{
...
set_system_gate(0x80,&system_call); //注册
}
system_call:
cmpl $nr_system_calls-1,%eax
ja bad_sys_call
push %ds
push %es
push %fs
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
movl $0x17,%edx # fs points to local data space
mov %dx,%fs //***
call sys_call_table(,%eax,4)
pushl %eax
movl current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
ret_from_sys_call:
movl current,%eax # task[0] cannot have signals
cmpl task,%eax
je 3f
cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
jne 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
jne 3f
movl signal(%eax),%ebx
movl blocked(%eax),%ecx
notl %ecx
andl %ebx,%ecx
bsfl %ecx,%ecx
je 3f
btrl %ecx,%ebx
movl %ebx,signal(%eax)
incl %ecx
pushl %ecx
call do_signal
popl %eax
3: popl %eax
popl %ebx
popl %ecx
popl %edx
pop %fs
pop %es
pop %ds
iret
//这是sys_call_table的表,根据传入eax的值找到对于的函数,
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_iam, sys_whoami };
添加系统调用
-
在kernal 的 makefile中增加文件
diff --git a/linux-0.11/kernel/Makefile b/linux-0.11/kernel/Makefile index 0afa1dc..2ae5152 100644 --- a/linux-0.11/kernel/Makefile +++ b/linux-0.11/kernel/Makefile @@ -26,7 +26,7 @@ CPP =gcc-3.4 -E -nostdinc -I../include 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 + signal.o mktime.o who.o kernel.o: $(OBJS) $(LD) -m elf_i386 -r -o kernel.o $(OBJS) @@ -48,6 +48,11 @@ dep: (cd blk_drv; make dep) ### Dependencies: +who.s who.o: who.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 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 \
-
编写在kernal中编写who.c文件,
#include <asm/segment.h> #include <errno.h> #include <linux/kernel.h> #define NAMELEN 23 char username[NAMELEN+1]; int sys_iam(const char *name) { unsigned int namelen = 0; int i = 0; int res = -1; //printk("Now we in kernel's sys_iam\n"); while (get_fs_byte(name+namelen) != '\0') namelen++; if (namelen <= NAMELEN) { //printk("All %d user space's chars to be copied to the kernel\n", namelen); //printk("Copying from user to kernel...\n"); for(i = 0; i < namelen; i++) { username[i] = get_fs_byte(name+i); } //printk("Done!\n"); username[i] = '\0'; //printk("%s\n", username); res = namelen; } else { printk("Error, the user space's name's length is %d longer than 23!\n", namelen); res = -(EINVAL); } return res; } int sys_whoami(char *name, unsigned int size) { unsigned int namelen = 0; int i = 0; int res = -1; //printk("Now we in kernel's sys_whoami\n"); while(username[namelen] != '\0') namelen++; if (namelen < size) { //printk("All %d kernel's chars to be copied to user space\n", namelen); //printk("Copying from kernel to user...\n"); for (i = 0; i < namelen; i++) { put_fs_byte(username[i], name+i); } //printk("Done!\n"); put_fs_byte('\0', name+i); res = namelen; } else { printk("Error, the kernel's name's length is longer than %d\n", size); res = -(EINVAL); } return res; }
-
修改几个宏
jewinh@ubuntu:~/oslab/linux-0.11$ git diff diff --git a/linux-0.11/include/linux/sys.h b/linux-0.11/include/linux/sys.h index c538fc1..1d1ef15 100644 --- a/linux-0.11/include/linux/sys.h +++ b/linux-0.11/include/linux/sys.h @@ -70,6 +70,8 @@ extern int sys_sgetmask(); extern int sys_ssetmask(); extern int sys_setreuid(); extern int sys_setregid(); +extern int sys_iam(); +extern int sys_whoami(); 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, @@ -83,4 +85,4 @@ 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_setreuid,sys_setregid, sys_iam, sys_whoami }; diff --git a/linux-0.11/include/unistd.h b/linux-0.11/include/unistd.h index bf71dcb..43af333 100644 --- a/linux-0.11/include/unistd.h +++ b/linux-0.11/include/unistd.h @@ -129,6 +129,8 @@ #define __NR_ssetmask 69 #define __NR_setreuid 70 #define __NR_setregid 71 +#define __NR_iam 72 +#define __NR_whoami 73 #define _syscall0(type,name) \ type name(void) \ diff --git a/linux-0.11/kernel/system_call.s b/linux-0.11/kernel/system_call.s index 05891e1..8b096d6 100644 --- a/linux-0.11/kernel/system_call.s +++ b/linux-0.11/kernel/system_call.s @@ -58,7 +58,7 @@ sa_mask = 4 sa_flags = 8 sa_restorer = 12 -nr_system_calls = 72 +nr_system_calls = 74 # origin is 72, now add sys_iam and sys_whoami
应用编写
通过上面操作,我们的内核已经有了两个系统调用的函数。
下面,我们在应用层去调用系统的函数。
iam.c
#define __LIBRARY__
#include <unistd.h>
#include <errno.h>
_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) {//参数多于2个
while ((name[namelen] = argv[1][namelen]) != '\0') //放到name中,
namelen++;
printf("iam.c: %s, %d\n", name, namelen);
res = iam(name);//把name传入。调用sys_iam
errno = EINVAL; //返回参数无效。
return res;
}
}
#define _syscall1(type,name,atype,a) \
type name(atype a) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(a))); \
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
return -1; \
}
int iam(char * name)
{
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_iam),"b" ((long)(name))); \
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
return -1; \
}
int sys_iam(const char *name) 做了什么?
通过get_fs_byte把用户空间的内容拷贝到内核空间。
在system_call中,fs=0x17 是本地数据空间。
通过从寄存器中读出东西,回写到内核空间来完成,最终保存到who.c 中的 char username[NAMELEN+1];
int sys_whoami(char *name, unsigned int size) 反过来,从内核空间把username的内容拷贝回来,
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));
}
linux0.11中的编译:
gcc -o iam iam.c -Wall
测试用例
注意用例3,当输入太长时,iam不会修改username的内容,因此还是跟第二次修改之后的内容一致。
#/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`
if [ "$result" = "$expected3" ]; then
echo PASS.
else
score3=0
echo FAILED.
fi
score=$score+$score3
let "totalscore=$score"
echo Score: $score = $totalscore%
总结
我们通过系统调用实现了一个库函数,这个库函数可以实现往内核的某个内存写入一些东西,然后从内核中再把这个东西读出来。