当前位置:文档之家› linux0.11系统调用原理及实验总结

linux0.11系统调用原理及实验总结

char N_MAX[26];
int sys_whoami(char* name, unsigned int size)
{
if(strlen(N_MAX)>size)
return -EINVAL;
int i;
for(i=0;N_MAX[i]!='\0';i++)
{
put_fs_byte(N_MAX[i],&name[i]);
那么下面就说这个宏干了什么,也就是说上面的那个“调用过程”是怎么样的呢?在这个宏中嵌入了汇编代码,做的工作就是int 0x80,其中将字符串“_NR_”和name连接,组成一个宏并将这个宏的值,也就是被调用的系统函数在sys_call_table中偏移量送到eax寄存器中;同时指明系统函数将来的返回值放到eax中。
}
return strlen(N_MAX);
}
int sys_iam(char *name)
{
char c;
char str[100];
memset(str,'\0',sizeof(str));
int i;
for(i = 0; i <= MAX; i++)
{
if((c=get_fs_byte(&name[i]))!='\0')
addr = &system_call = 0x7119通过查找system.map可以查到中断处理程序的地址
*/
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \
"movw %0,%%dx\n\t" \
#define _syscall1(type,name,atype,a) \
type name__res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(a))); \
"movw %0,%%dx\n\t"
字操作16位操作,操作低16位,
高16位已经有调用函数的地址.
"movl %%eax,%1\n\t"
双字操作,为门的0--31位赋值
"movl %%edx,%2\n\t"
双字操作,为门的32--63位赋值
3.2以_syscall1为例,分析系统调用入口宏的含义。
其中_syscall1是一个宏,在include/unistd.h中定义。将_syscall1(int,close,int,fd)进行宏展开,可以得到:
调用接口宏含义说明:
它先将宏__NR_##name存入EAX,将参数fd存入EBX,然后进行0x80中断调用。调用返回后,从EAX取出返回值,存入__res,再通过对__res的判断决定传给API的调用者什么样的返回值。__NR_##name就是系统调用的编号,在include/unistd.h中定义;在上面的例子中,我们添加了两个自己的系统调用接口,如下:
printf("a=%d", a);
a = whoami(cc,8);
printf("iam=%s\n",cc);
return(1);
}
3系统调用相关代码的分析
3.1初始化软件中断门。
3.1.1函数调用层次
初始化软件中断门,就是把0x80软件中断的处理函数system_call挂载到中断向量表idt中,以确保发生软件中断时会运行system_call函数,这个函数在system_call.s实现。初始化的流程如下:
"d" ((char *) (addr)),"a" (0x00080000))
3.1.3分析初始化宏_set_gate的实现
__asm__格式为嵌入式汇编的格式,分析可知代码有5个传入的参数%0,%1,%2,%3,%4如下:
%0,立即数
"i" ((short) (0x8000+(dpl<<13)+(type<<8)))
{
str[i] = c;
}
else
break;
}
if((c != '\0') ||(i>MAX))
{
return -EINVAL;
}
memset(N_MAX,'\0',sizeof(N_MAX));
for(i=0;str[i]!='\0';i++)
{
N_MAX[i]=str[i];
}
return i;
#define __NR_iam73
int whoami(void);
intaim(void);
2.2.2在exit.c文件中添加系统调用处理函数的实现
系统调用的函数可以在其他.c文件中添加或在新建文件中添加,只要编辑进image都是可以的,这里为了调试方便就在exit.c文件中添加了。
#define MAX 23
完成的功能是将字符串参数name的内容拷贝到内核中保存下来。要求name的长度不能超过23个字符。返回值是拷贝的字符数。如果name的字符个数超过了23,则返回“-1”,并置errno为EINVAL。
第二个系统调用是whoami(),其原型为:
int whoami(char* name, unsigned int size);
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
return -1; \
}
传入参数说明:
其中type表示系统调用的返回值类型,name表示该系统调用的名称,atype、a分别表示第1个参数的类型和名称;可以有n个系统调用的传入参数,它们的数目和_syscall后面的数字一样大。
#define __NR_whoami72
#define __NR_iam73
3.3对_system_call函数的分析
处理流程图
处理流程分析
_system_call:
cmpl $nr_system_calls-1,%eax #调用号如果超出范围的话就在eax中置-1并退出。
ja bad_sys_call
所以,系统调用过程是这样的:
应用程序调用libc中的函数->libc中的函数引用系统调用宏->系统调用宏中使用int 0x80完成系统调用并返回。
另外一种访问内核的方式是直接添加一个系统调用,供自己的应用程序使用,这样就不再使用库函数了,变得更为直接,效率也会更高。
1.2相关的数据结构
在说具体的调用过程之前,这里先要说几个数据结构。
_syscall1(int,iam,char*,name)
_syscall2(int,whoami,char*,name, unsigned int,size)
int main()
{
int a = 0;
char bb[26] = "champion";
char cc[26] = "";
a = iam(bb);
L
1系统调用的原理
1.1概述
系统调用是一个软中断,中断号是0x80,它是上层应用程序与Linux系统内核进行交互通信的唯一接口。通过int 0x80,就可使用内核资源。不过,通常应用程序都是使用具有标准接口定义的C函数库间接的使用内核的系统调用,即应用程序调用C函数库中的函数,C函数库中再通过int 0x80进行系统调用。
1.2.3系统调用宏
系统调用宏_syscalln(type,name)在内核的unistd.h文件中定义的,对它展开就是:
type name(参数列表)
{
调用过程;
};
其中,n为参数个数,type为函数返回值类型,name为所要调用的系统函数的名字。在unistd.h中共定义了4个这样的宏(n从0到3),也就是说,0.11核中系统调用最多可带3个参数。
它将内核中由iam()保存的名字拷贝到name指向的用户地址空间中,同时确保不会对name越界访存(name的大小由size说明)。返回值是拷贝的字符数。如果size小于需要的空间,则返回“-1”,并置errno为EINVAL。
2.2代码添加修改步骤
2.2.1在unistd.h中添加系统调用接口
#define __NR_whoami72
1.3系统调用处理过程
下面我再说一下系统调用的核心软中断int 0x80具体干了什么。这条指令会引起CPU的软件中断,cpu会根据中断号找到中断处理程序。这个中断处理程序是在System_call.s中。在中断处理程序的工作过程大致是这样的:
1.3.1将寄存器ds,es,fs以及存有参数的edx,ecx,ebx入栈,再ds,es,指向内核段,fs指向用户段。
1.2.1系统调用函数表
系统调用函数表sys_call_table是在sys.h中定义的,它是一个函数指针数组,每个元素是一个函数指针,它的值是各个系统提供的供上层调用的系统函数的入口地址。也就是说通过查询这个表就可以调用软中断0x80所有的系统函数处理函数。
1.2.2函数指针偏移宏
这是一系列宏,它们的定义在unistd.h中,基本形式为#define _NR_name value,name为系统函数名字,value是一个整数值,是name所对应的系统函数指针在sys_call_table中的偏移量。
相关主题