李治军操作系统实验2——系统调用

操作系统实验2——系统调用

代码仓库

GitLab

实验内容

给linux-0.11添加两个系统调用,并在程序中使用他们。

系统调用处理过程

在实验开始之前,我们先来了解一下系统调用是怎么进行的。

如果我们想使用C函数库调用close()系统调用,可以直接在C程序中写:

//int read(int fd);
read(fd);

如果想直接一点,不通过C函数库,可以这样写:

#define __LIBRARY__
#include <unistd.h>

_syscall1(int,close,int,fd)

宏函数_syscalln() 被定义在include/unistd.h中(n是参数数量,0-3)。

#include/unistd.h:172
#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
	return (type) __res; \
errno=-__res; \
return -1; \
}

_syscall1(int,close,int,fd)宏展开后

int close(int fd) 
{ 
    long __res;      
    __asm__ volatile ("int $0x80" 
        : "=a" (__res) 
        : "0" (__NR_close),"b" ((long)(fd)));      
    if (__res >= 0)
        return (int) __res; 
    errno = -__res; 
    return -1; 
}

宏__NR_close被定义在同文件66行,也可以看到其它被定义的系统调用功能号。

//include/unistd.h:64
#define __NR_write	4
#define __NR_open	5
#define __NR_close	6
#define __NR_waitpid	7
#define __NR_creat	8

函数先将宏__NR_close存入EAX(功能号),将参数fd存入EBX,然后进行0x80中断调用。调用返回后,从EAX取出返回值,存入__res,再通过对__res的判断决定传给API的调用者什么样的返回值。

根据IDT(中断描述符表),中断发生后,自动调用函数system_call。

进入内核中的系统调用处理程序kernel/system_call.s的代码会首先检查EAX中的系统调用功能号是否合法,然后根据sys_call_table[]调用相应的系统调用处理程序。

检查EAX系统调用号是否合法。

#kernel/system_call.s:61
nr_system_calls = 72
#kernel/system_call.s:94
cmpl $nr_system_calls-1,%eax

调用地址在_sys_call_table + %eax * 4处的函数。

#kernel/system_call.s:94
call sys_call_table(,%eax,4)

sys_call_table[]函数指针表在include/linux/sys.h。

//include/linux/sys.h:74
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, ......};

这些函数都被声明在本文件的开头。

//include/linux/sys.h:1
extern int sys_setup();
extern int sys_exit();
extern int sys_fork();
extern int sys_read();
extern int sys_write();
......

至此我们终于完成了一个系统调用!

实验步骤

1.修改系统调用函数总数,我们要添加两个系统调用函数,所以把原本的72改为74。

#kernel/system_call.s:61
nr_system_calls = 74

2.添加函数声明,修改系统调用函数指针表。添加sys_iam()和sys_whoami()的声明。在系统调用函数指针表sys_call_table[] 后加上:sys_iam和sys_whoami。

//include/linux/sys.h:71
......
extern int sys_setreuid();
extern int sys_setregid();
extern int sys_iam();
extern int sys_whoami();
//include/linux/sys.h:76
fn_ptr sys_call_table[] = {......,sys_ssetmask,
sys_setreuid,sys_setregid,sys_iam,sys_whoami };

3.实现sys_iam()和sys_whoami()。在kernel中添加who.c。使用get_fs_byte()和put_fs_byte()实现用户态和核心态之间传递数据。

//kernel/who.c
#include <string.h>
#include <errno.h>
#include <asm/segment.h>

char msg[24]; 

int sys_iam(const char *name)
{
    int i;
    char tmp[30];
    for (i = 0; i < 30; i++)
    {
        //从用户态内存取得数据
        tmp[i] = get_fs_byte(name + i);
        if (tmp[i] == '\0')
            break; 
    }
    i = 0;
    while (i < 30 && tmp[i] != '\0')
        i++;
    if (i > 23)
    {
        // printk("String too long!\n");
        return -(EINVAL); //置errno为EINVAL 并返回-1
    }
    strcpy(msg, tmp);
    return i;
}

int sys_whoami(char *name, unsigned int size)
{
    int len = 0;
    for (; msg[len] != '\0'; len++)
        ;
    if (len > size)
    {
        return -(EINVAL);
    }
    int i = 0;
    for (i = 0; i < size; i++)
    {
        put_fs_byte(msg[i], name + i);
        if (msg[i] == '\0')
    }
    return i;
}

4.修改kernel/Makefile,这样who.c就可以一起编译了。

//kernel/Makefile:29
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
//kernel/Makefile:50
### Dependencies:
who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.h

5.完成编译系统后。启动linux-0.11,修改根目录下/usr/include/unistd.h(如果直接修改linux-0.11源码下的/include/unistd.h,这边并不会跟着修改)。

为我们添加的两个系统调用添加系统调用编号宏。

//NOTE:非源码文件!
//usr/include/unistd.h:130
#define __NR_setreuid	70
#define __NR_setregid	71
#define __NR_iam	72
#define __NR_whoami	73

6.编写测试程序验证新添加的系统调用。编译运行测试程序,带一个参数name字符串,若能正常执行系统调用,应输出我们的参数name。

#include <errno.h>
#define __LIBRARY__
#include <unistd.h>
#include <stdio.h>

_syscall1(int, iam, const char*, name);
_syscall2(int, whoami, char*, name, unsigned int ,size);

int main(int argc,char ** argv)
{
    char s[30];
    iam(argv[1]);
    whoami(s,30);
    printf("%s\n",s);
    return 0;
}
posted @ 2021-02-27 17:51  ithepug  阅读(406)  评论(0)    收藏  举报