zoukankan      html  css  js  c++  java
  • [HIT操作系统][lab2]添加系统调用

    我的ID: 丶爆炸的榴莲,aweffr

    实验链接(lab2):

    https://www.shiyanlou.com/courses/115/labs/569/document

    课程链接:

    http://mooc.study.163.com/course/HIT-1000002004#/info

    感谢网友Tradoff,参考了你详尽的实验报告:

    http://www.cnblogs.com/tradoff/p/5734582.html

    -------------------------------实验内容-------------------------------

    在Linux 0.11上添加两个系统调用,并编写两个测试程序。

    两个系统调用:

    1. int iam(const char * name);

    2. int whoami(char * name, unsigned int size);

    测试程序:

    编写iam.c,调用iam接口,生成的可执行iam接收一个字符串参数,把名字写进内核。

    编写whoami.c,调用whoami接口,把名字从内核中拷贝到程序中并打印。

    实验报告需要回答两个问题:

    1. 从Linux 0.11现在的机制看,它的系统调用最多能传递几个参数?你能想出办法来扩大这个限制吗?

    2. 用文字简要描述向Linux 0.11添加一个系统调用foo()的步骤。

    实验的一点小感受:

    实验中的难题的答案其实都已经在实验提示中了。答案就在眼前,只是一开始看不到,需要多看多想很多遍才get。

    -------------------------------思路/前言-------------------------------

    做这个实验之前首先要明白一个问题,就是用户空间和内核空间的机制。

    正常情况下,写一个C程序,我们面对的地址空间是系统虚拟化给我们的一个独立的、连续的地址空间,当程序中我们写了”printf”,会触发系统中断,程序会“陷入”内核态,由操作系统大管家接管一些事情,做完之后再把控制权还回来,对我们程序而言除了接收到一个返回值之外,是没有别的的感觉的。我们正常情况下写的程序就像熊孩子在家里,饿了吃饭乏了睡觉精神饱满就可劲造,家长(操作系统)默默烧饭(printf)打扫收拾(回收资源)给零花钱(malloc),给熊孩子一种富二代的错觉。然后小孩子摸了不该摸的或者破坏东西了就吊起来打一顿惩罚(segment fault, kill process)。

    而在内核态程序中就不一样了,以我的内核中的iam程序为例:

     

    当系统执行到14行时,如果我们直接解引用(name + i),得到的东西是未知的,因为此时操作系统正处于内核态,而传进来的name指针所存储的是用户态下地址空间的地址值,直接解引用不知道会解出什么(大概是个segment fault?)。

    而内核态和用户态的转换机制,需要结合“段页内存管理”的相关知识点。就本实验来说,需要知道的就是:

    当我们的用户程序调用了一个系统调用,kernel/system_call.s里的纯汇编代码就会把ds, ex设置成0x10,就是指向内核地址空间。并且把fs这是成17。然后就去调用我们写的系统调用函数了。

    而从内核空间拿到正确的用户空间数据的方法,要去看<asm/segment.h>中给的函数。

    get_fs_byte()函数的实现如下:

    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;
    }

    这个函数就是临时改变fs寄存器,然后把正确的结果塞到_v寄存器里,然后返回正确的值。

    此外就是实际编写代码时,会在#include <unistd.h>之前先预定一个宏__LIBRARY__。目的在于使用这个宏后, _syscall1等展开的宏才有效。

    如代码

    _syscall1(int, iam, const char *, name)

    通过gcc打开-E选项查看预编译后的文件,这句代码被替换成了

    int iam( const char * name) {
         long __res;
         __asm__ volatile (
             "int $0x80" : "=a" (__res) : "0" ( 72 ),"b" ((long)( name))
            );
        if (__res >= 0)
            return (int) __res;
        errno = -__res;
        return -1;
    }

     (我在unistd.h中把`iam`调用的调用号定义成72了)

    -------------------------------具体步骤-------------------------------

    修改include/linux/sys.h:

    添加声明:

    extern int sys_iam();
    extern int sys_whoam();

    在sys_call_table数组末尾添加两个函数指针:

    ..., sys_iam, sys_whoami}

    修改include/unistd.h:

    添加调用号:

    #define __NR_iam    72
    #define __NR_whoami    73

    声明给用户调用的函数:

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

    修改kernel/system_call.s:

    修改调用个数:

    nr_system_calls = 74

    添加kernel/who.c:

    #define __LIBRARY__
    #include <asm/segment.h>
    #include <errno.h>
    #include <unistd.h>
    
    #define MAX_SIZE 23
    
    int username_size = 0;
    char username[MAX_SIZE + 1];
    
    int sys_iam(const char *name)
    {
        /* Get the name length */
        int i = 0;
        while (get_fs_byte(name + i) != '') i++;
        printk("sys_iam:
     name size is %d 
    ", i);
        if (i>MAX_SIZE)
            return -EINVAL;
        else
            username_size = i;
        /* Copy bytes from user space */
        int j;
        for (j = 0; j <= username_size; j++)
            username[j] = get_fs_byte(name + j);
        return username_size;
    }
    
    int sys_whoami(char *name, unsigned int size)
    {
        if (username_size > size)
            return -EINVAL;
        /* Copy char val to the user space */
        int i;
        for (i = 0; i <= username_size; i++)
            put_fs_byte(username[i], name + i);
        return username_size;
    }

    修改kernel/Makefile:

    添加编译who的规则:

    OBJS = ... who.o
    ### Dependencies: who.s who.o: who.c ../include/unistd.h ../include/asm/segment.h ../include/errno.h
    ...

    测试文件iam.c和whoami.c

    挂载hdc,在~/oslab/hdc/usr/root/目录下添加iam.c和whoami.c

    /* iam.c */
    #include <stdio.h>
    #define __LIBRARY__
    #include <unistd.h>
    
    _syscall1(int, iam, const char *, name)
    
    int main(int argc, char * argv[])
    {
        if (argc <= 1)
        {
             printf("error: input your name, please!
    ");
             return -1;
        }
    
        if (iam(argv[1]) == -1)
        {
             printf("error: length limit is 23. 
    ");
             return -1;
        }
        return 0;
    }

     

    /* whoami.c */
    
    #include <stdio.h>
    #define __LIBRARY__
    #include <unistd.h>
    
    _syscall2(int, whoami, char *, name, unsigned int, size)
    
    int main(int argc, char * argv[])
    {
        char name[24];
        if (whoami(name, 23) == -1)
        {
             printf("error while reading name...");
             return -1;
        }
        printf("%s
    ", name);
        return 0;
    }

    -------------------------------测试结果-------------------------------

    将实验提供的测试评分文件:testlab2.c testlab2.sh放到~/oslab/hdc/usr/root/目录下。

    在bochs里编译whoami.c, iam.c和testlab2.c后,运行

    ./testlab2

    截图如下:

     

    sh testlab2.sh

    截图如下:

     

    -------------------------------思考题-------------------------------

    一. 从Linux 0.11现在的机制看,它的系统调用最多能传递几个参数?你能想出办法来扩大这个限制吗?

    根据sys_call.s可知最多三个参数,分别由ebx, ecx, edx储存。

     

    我认为比较好的做法是,由系统调用的地方定义一个用于传参的结构体。用户程序调用该系统调用时,传入结构体的指针。这个思路类似于多线程中pthread_create的传参方法。

    此外可以通过位运算的方法,高16位存一个short,低16位存另一个short,合并进一个寄存器,不过这种方式可读性不好,需要操作系统和程序员形成默契,容易出错。

    二. 用文字简要描述向Linux 0.11添加一个系统调用foo()的步骤。

    1. 在include/unistd.h中定义宏__NR_foo。声明给用户调用的函数 int foo()。

    2. 修改kernel/system_call.s,使得其中nr_system_calls增加1。

    3. 在inlcude/linux/sys.h中添加函数声明 extern int sys_foo(), 在系统调用入口表fn_ptr末端添加元素sys_foo;

    4. 添加kernel/foo.c,做foo()的具体实现代码。

    5. 修改kernel/Makefile,在OBJS中加入foo.o,在依赖(### Dependencies:)中添加foo.s、foo.o的依赖项。

    以上就是完整报告,祝大家实验顺利~

  • 相关阅读:
    [整机笔记][转贴]硬盘无法双击打开的解决办法
    [网络随摘][转载]值得用一生回味的经典语录
    [网络随摘][转载]如果你已经过了20还不到25岁
    [网络随摘][转摘]只有十句话,我却看了十分钟
    实现百台手机异步并发定时自动GPS定位打卡,基于python全天后定时签到稳定版。
    【FireFox】在Firefox中,关于隐藏table中某一行tr,其他td的边框显示异常
    【记】屏蔽浏览器shift+鼠标滚轴事件
    【记】Javascript的函数直接量定义
    【Javascript】Javascript中的函数调用模式
    【记】three.js的一个简单的代码记录
  • 原文地址:https://www.cnblogs.com/aweffr/p/7525840.html
Copyright © 2011-2022 走看看