系统调用笔记
用户态只能通过系统调用去访问内核,即只能通过中断处理访问, INT 0X80
就是我们的C语言程序,调用一个在unistd.h定义的宏,_syscall*()*代表的是参数的个数,这个宏的意义差不多等于创建一个函数。这个函数只有一个代码就是int 0x80。
系统调用read,宏展开后(就是创建了一个read函数)
int 0x80是由main.c中sched_init()创建的。其实就是初始化好了IDT表
INT 0X80会根据进入内核,调用system_call. system_call 就会去根据__NR_##name去查表执行相应的处理函数
实验记录
实验要求我们添加两个系统调用,iam()和whoami()
运行结果
$ ./iam lizhijun
$ ./whoami
lizhijun
1
首先是编写最外层的用户程序iam.c和whoami.c
注意 _syscall 要写在 #define __ LIBRARY __ #include<unistd.h>下面
iam.c
whoami.c
2
有了用户程序,我们就要编写系统调用sys_iam和sys_whoami
在linux-0.11/kernel下添加who.c
#define __LIBRARY__
#include <unistd.h>
#include <errno.h>
#include <asm/segment.h>
char temp[64]={0};
int sys_iam(const char* name)
{
int i=0;
while(get_fs_byte(name+i)!='\0') //从用户态获取一个字符并返回这个字符
i++;
if(i>23){
return -EINVAL;
}
printk("%d\n",i);
i=0;
while((temp[i]=get_fs_byte(name+i))!='\0'){
i++;
}
return i;
}
int sys_whoami(char* name,unsigned int size)
{
int i=0;
while (temp[i]!='\0')
i++;
if (size<i)
return -1;
i=0;
while(temp[i]!='\0'){
put_fs_byte(temp[i],(name+i));//把内核态的一个字符存到用户态程序中,也就是name->temp
i++;
}
return i;
}
3 修改相关的头文件
linux-0.11/include/linux/sys.h
这个头文件修改的意义是把 iam与whoami两个函数加到全局变量,和中断函数表中就可以了,中断被调用的时候,先查找中断向量表,
找到相应的函数名,调用其函数。
linux-0.11/kernel/system_call.s
这里要修个nr_system_calls 本来是72 也就是有72个系统调用函数,我们加了两个所以改成74
hdc/usr/include/unistd.h
unistd.h 不能直接在oslab直接直接修改,而需要在虚拟机中修改,在oslab中有一个mount-hdc脚本
运行sudo ./mount-hdc 可以把虚拟机硬盘挂载在oslab/hdc 目录下。
在hdc/usr/include 目录下修改unistd.h
4 就是修改linux-0.11/kernel下的MakeFile
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
改为:
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
另一处:
Dependencies:
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
…/include/linux/kernel.h …/include/linux/tty.h …/include/termios.h
…/include/asm/segment.h
改为:
Dependencies:
who.s who.o: who.c …/include/linux/kernel.h …/include/unistd.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
…/include/linux/kernel.h …/include/linux/tty.h …/include/termios.h
…/include/asm/segment.h
Makefile修改后,和往常一样“make all”就能自动把who.c加入到内核中了。如果编译时提示who.c有错误,就说明修改生效了。所以,有意或无意地制造一两个错误也不完全是坏事,至少能证明Makefile是对的。
5 编译运行
编译必须在虚拟机下编译,因为你修改的unistd.h只在虚拟机生效
gcc iam.c -o iam
gcc whoami.c -o whoami
总结
添加一个系统调用主要是
1.编写用户程序,这个用户程序需要包含_syscall* 宏(创建一个函数),然后调用这个函数
2.编写系统调用函数放到linux-0.11/kernel
3.各种相关头文件的修改
linux-0.11/include/linux/sys.h添加extern int sys_xxx和添加syscall_table[]
linux-0.11/kernel/system_call.s 修改nr_system_calls(syscall_table中的系统调用函数个数)
hdc/usr/include/unistd.h 其实也就是编译的库文件 添加__ NR_xx 系统调用号
linux-0.11/kernel下的MakeFile修改OBJS和Dependencies
参考博客
https://blog.csdn.net/wangyi_lin/article/details/6921110
https://www.cnblogs.com/coderlynn/p/9127632.html
实验指导书
https://hoverwinter.gitbooks.io/hit-oslab-manual/content/sy2_syscall.html