当前位置: 代码迷 >> 综合 >> linux0.11_系统调用
  详细解决方案

linux0.11_系统调用

热度:97   发布时间:2023-12-15 12:02:44.0

系统调用

系统调用的过程

void sched_init(void)
{
    ...set_system_gate(0x80,&system_call); //注册
}system_call:cmpl $nr_system_calls-1,%eaxja bad_sys_callpush %dspush %espush %fspushl %edxpushl %ecx		# push %ebx,%ecx,%edx as parameterspushl %ebx		# to the system callmovl $0x10,%edx		# set up ds,es to kernel spacemov %dx,%dsmov %dx,%esmovl $0x17,%edx		# fs points to local data spacemov %dx,%fs  //***call sys_call_table(,%eax,4)pushl %eaxmovl current,%eaxcmpl $0,state(%eax)		# statejne reschedulecmpl $0,counter(%eax)		# counterje reschedule
ret_from_sys_call:movl current,%eax		# task[0] cannot have signalscmpl task,%eaxje 3fcmpw $0x0f,CS(%esp)		# was old code segment supervisor ?jne 3fcmpw $0x17,OLDSS(%esp)		# was stack segment = 0x17 ?jne 3fmovl signal(%eax),%ebxmovl blocked(%eax),%ecxnotl %ecxandl %ebx,%ecxbsfl %ecx,%ecxje 3fbtrl %ecx,%ebxmovl %ebx,signal(%eax)incl %ecxpushl %ecxcall do_signalpopl %eax
3:	popl %eaxpopl %ebxpopl %ecxpopl %edxpop %fspop %espop %dsiret//这是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../includeOBJS  = 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.okernel.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.hexit.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 23char 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 = 4sa_flags = 8sa_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_iamerrno = 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/shstring1="Sunner"
string2="Richard Stallman"
string3="This is a very very long string!"score1=10
score2=10
score3=10expected1="Sunner"
expected2="Richard Stallman"
expected3="Richard Stallman"echo Testing string:$string1
./iam "$string1"
result=`./whoami`
if [ "$result" = "$expected1" ]; thenecho PASS.
elsescore1=0echo FAILED.
fi
score=$score1echo Testing string:$string2
./iam "$string2"
result=`./whoami`
if [ "$result" = "$expected2" ]; thenecho PASS.
elsescore2=0echo FAILED.
fi
score=$score+$score2echo Testing string:$string3
./iam "$string3"
result=`./whoami`
if [ "$result" = "$expected3" ]; thenecho PASS.
elsescore3=0echo FAILED.
fi
score=$score+$score3let "totalscore=$score"
echo Score: $score = $totalscore%

总结

我们通过系统调用实现了一个库函数,这个库函数可以实现往内核的某个内存写入一些东西,然后从内核中再把这个东西读出来。