我的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) != '