linux0.11_系统调用

系统调用

系统调用的过程

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%

总结

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

猜你喜欢

转载自blog.csdn.net/qq_38307618/article/details/124273598
今日推荐