哈工大操作系统实验2:添加系统调用iam()、whoami()
需要添加到内核中的文件:
lib/whoami.c
、lib/iam.c
、kernel/who.c
需要修改的内核文件:
include/unistd.h
、include/linux/sys.h
、kernel/system_call.s
、lib/Makefile
、kernel/Makefile
为测试而添加的用户程序:
iam.c
、whoami.c
-
在
include/linux/sys.h
中的_sys_call_table
表内添加两个新的系统调用函数,以及两个extern...
;如下所示: -
表项中有了两个新增的函数
sys_iam
和sys_whoami
,我们倒着推一下,是谁会调用这两个函数呢?答案是kernel/system_call.s
,修改该汇编代码;如下所示: -
修改
include/unistd.h
,在其中添加两个系统调用号,正是有了系统调用号,才可以决定int 0x80
的具体走向。 -
系统调用通过内嵌汇编方式调用
int 0x80
中断,进入了上述的system_call.s
再进一步找到_sys_call_table
,根据系统调用号在表中找到真正的内核代码(或者内核函数)地址,也即sys_iam()
和sys_whoami()
.①话说回来,用户级程序通过
系统调用
引发int 0x80
中断,进入系统内核;所以现在我们添加这些可以被用户程序直接调用的接口,也即whoami()
、iam()
,这里我是模仿lib/write.c
的②添加文件
lib/whoami.c
、lib/iam.c
whoami.c
文件内容如下:#define __LIBRARY__ #include <unistd.h> _syscall2(int, whoami, char *, name, unsigned int, size) /* * 该宏展开就是函数int whoami(char* name, unsigned int size) * 头文件<unistd.h>中定义了该宏的展开形式和规则,其通过内嵌汇编的方式调用中断 */
iam.c
文件内容如下:#define __LIBRARY__ #include <unistd.h> _syscall1(int, iam, const char *, name) /* * 该宏展开就是函数int iam(const char* name) */
-
有了上述的准备,用户程序可以调用系统接口(第4步),展开成内嵌汇编进入系统内核;然后根据系统调用号(第3步)的分流引导,从
kernel/system_call.s
跳转到include/linux/sys.h
中的_sys_call_table
表,执行 真正的系统内核函数sys_iam
和sys_whoami
,因此接下来是对这两个系统内核函数的具体实现添加实现文件
kernel/who.c
其文件内容如下:/* * 函数sys_iam()和sys_whoami()的实现 */ #include <unistd.h> #include <asm/segment.h> /*get_fs_byte() put_fs_byte()函数调用的头文件*/ #include <errno.h> /*错误代码 EINVAL等要用到*/ #include <string.h> /*C语言字符串处理头文件*/ /*实现方式1:(自己写的)*/ char kernelchars[23]; int sys_iam(char *name) { char temp[30]; int counter = 0; for (; counter < 30; counter++) { temp[counter] = get_fs_byte(&name[counter]); if (temp[counter] == '\0') break; } if (counter >= 23) { errno = EINVAL; return -1; } strcpy(kernelchars, temp); return counter + 1; } /* * 功能:将内核中由iam()保存的字符串读取出来,保存到name指向的用户空间(该用户空间应预留'\0'的位置),同时确保不会对name访问越界 * 返回值:复制的字符数,包含'\0' * 若size小于需要的空间,则返回-1,并设置errno=EINVAL */ int sys_whoami(char *name, unsigned int size) { unsigned int counter = 0; char temp[24]; int j = 0; temp[counter] = kernelchars[counter]; while (temp[counter] != '\0') { counter++; temp[counter] = kernelchars[counter]; } if (size < counter + 1){ errno = EINVAL; return -1; } for(; j < counter; j++){ put_fs_byte(temp[j], &name[j]); } return counter + 1; } /***********************************************************************/ /*实现方式2:(调试的时候参考网上小伙伴的)*/ /*char msg[24]; int sys_iam(const char * name) { char tep[26]; int i = 0; for(; i < 26; i++) { tep[i] = get_fs_byte(name+i); if(tep[i] == '\0') break; } if (i > 23) return -(EINVAL); strcpy(msg, tep); 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') break; } return i; }*/
-
内核代码的修改基本完成了,接下来是为编译做准备的修改
Makefile
①因为在lib中添加了新的文件(第4步),所以应该修改一下
lib/Makefile
,这里也是模仿的write.c的依赖写法,具体如下所示:②因为新添加了
kernel/who.c
文件,所以kernel/Makefile
也需要修改,具体如下:
-
在宿主机重新编译Linux内核0.11
-
在宿主机器上挂载
hdc
—修改hdc/usr/include/unistd.h
(其实就是添加上两个系统调用号,如下所示)–卸载hdc
-
编写用户级别的测试程序
①第一种:单个文件同时调用测试之前添加的系统调用
#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; }
②第二种:分开测试,写一个测试
iam()
的iam.c
和一个测试whoami()
的whoami.c
/*iam.c如下:*/ #define __LIBRARY__ #include <unistd.h> _syscall1(int,iam,const char*,name); int main(int argc, char* argv[]){ iam(argv[1]); return 0; } /********************************************************/ /*whoami.c如下:*/ #define __LIBRARY__ #include <stdio.h> #include <unistd.h> _syscall2(int, whoami, char*, name, unsigned int, size); int main(int argc, char* argv[]){ char name[13]; whoami(name,sizeof(name)); printf("%s\n", name); return 0; }
-
在Bochs中模拟器中运行
Linux 0.11
,先编译然后测试① 单个测试文件
②两个测试文件时