📄 interrupt-sys_call_6.htm
字号:
\<BR> if ((unsigned long)(res) >= (unsigned long)(-125)) { \<BR>
errno = -(res); \<BR> res = -1; \<BR> } \<BR> return (type)
(res); \<BR>} while (0)</FONT></P>
<P><FONT face="Courier New" size=2>/* 没有参数的系统调用使用此Macro进行定义 */<BR>#define
_syscall0(type,name) \<BR>type name(void) \<BR>{ \<BR>long __res; \<BR>__asm__
volatile ("int $0x80" \<BR> : "=a" (__res) \<BR> : "0" (__NR_##name));
\<BR>__syscall_return(type,__res); \<BR>}</FONT></P>
<P><FONT face="Courier New" size=2>/* 具有1个参数的系统调用使用此Macro进行定义 */</FONT></P>
<P><FONT face="Courier New" size=2>#define _syscall1(type,name,type1,arg1)
\<BR>type name(type1 arg1) \<BR>{ \<BR>long __res; \<BR>__asm__ volatile ("int
$0x80" \<BR> : "=a" (__res) \<BR> : "0" (__NR_##name),"b"
((long)(arg1))); \<BR>__syscall_return(type,__res); \<BR>}</FONT></P>
<P><FONT face="Courier New" size=2>/* 具有2个参数的系统调用使用此Macro进行定义 */</FONT></P>
<P><FONT face="Courier New" size=2>#define
_syscall2(type,name,type1,arg1,type2,arg2) \<BR>type name(type1 arg1,type2 arg2)
\<BR>{ \<BR>long __res; \<BR>__asm__ volatile ("int $0x80" \<BR> : "=a"
(__res) \<BR> : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)));
\<BR>__syscall_return(type,__res); \<BR>}</FONT></P>
<P><FONT face="Courier New" size=2>/* 具有3个参数的系统调用使用此Macro进行定义 */</FONT></P>
<P><FONT face="Courier New" size=2>#define
_syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \<BR>type name(type1
arg1,type2 arg2,type3 arg3) \<BR>{ \<BR>long __res; \<BR>__asm__ volatile ("int
$0x80" \<BR> : "=a" (__res) \<BR> : "0" (__NR_##name),"b"
((long)(arg1)),"c" ((long)(arg2)), \<BR> "d" ((long)(arg3)));
\<BR>__syscall_return(type,__res); \<BR>}</FONT></P>
<P><FONT face="Courier New" size=2>/* 具有4个参数的系统调用使用此Macro进行定义 */</FONT></P>
<P><FONT face="Courier New" size=2>#define
_syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \<BR>type name
(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \<BR>{ \<BR>long __res;
\<BR>__asm__ volatile ("int $0x80" \<BR> : "=a" (__res) \<BR> : "0"
(__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \<BR> "d"
((long)(arg3)),"S" ((long)(arg4))); \<BR>__syscall_return(type,__res); \<BR>}
</FONT></P>
<P><FONT face="Courier New" size=2>/* 具有5个参数的系统调用使用此Macro进行定义 */</FONT></P>
<P><FONT face="Courier New" size=2>#define
_syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,
\<BR> type5,arg5) \<BR>type name (type1 arg1,type2 arg2,type3
arg3,type4 arg4,type5 arg5) \<BR>{ \<BR>long __res; \<BR>__asm__ volatile ("int
$0x80" \<BR> : "=a" (__res) \<BR> : "0" (__NR_##name),"b"
((long)(arg1)),"c" ((long)(arg2)), \<BR> "d" ((long)(arg3)),"S"
((long)(arg4)),"D" ((long)(arg5))); \<BR>__syscall_return(type,__res);
\<BR>}</FONT></P>
<P><FONT face="Courier New" size=2>/* 具有6个参数的系统调用使用此Macro进行定义 */</FONT></P>
<P><FONT face="Courier New" size=2>#define
_syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,
\<BR> type5,arg5,type6,arg6) \<BR>type name (type1 arg1,type2
arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6) \<BR>{ \<BR>long __res;
\<BR>__asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; movl %1,%%eax ; int
$0x80 ; pop %%ebp" \<BR> : "=a" (__res) \<BR> : "i" (__NR_##name),"b"
((long)(arg1)),"c" ((long)(arg2)), \<BR> "d" ((long)(arg3)),"S"
((long)(arg4)),"D" ((long)(arg5)), \<BR> "0" ((long)(arg6)));
\<BR>__syscall_return(type,__res); \<BR>}</FONT></P>
<P><FONT face="Courier New" size=2>/* 下面是几个通过上述Macro进行定义的系统调用的例子 */</FONT></P>
<P><FONT face="Courier New" size=2>static inline
_syscall0(pid_t,setsid)<BR>static inline _syscall1(int,dup,int,fd)<BR>static
inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)<BR>static inline
_syscall3(int,read,int,fd,char *,buf,off_t,count)<BR>static inline
_syscall3(off_t,lseek,int,fd,off_t,offset,int,count)</FONT></P>
<P><FONT face="Courier New"
size=2>Linux捕获到这个中断后,将执行其对应的中断服务程序(system_call),system_call首先将必要的寄存器(包括这6个通用寄存器)push到栈中,然后以%eax中的值作为索引到其所维护的系统调用功能函数入口表(sys_call_table)中查找并调用相应函数。等系统调用完成后,Linux内核将返回值装入EAX寄存器(如果不是void的话),但不用其它寄存器来返回执行结果。下面是Linux
2.4相关代码。</FONT></P>
<P><FONT face="Courier New" size=2>/* 这里定义的是Stack中各个寄存器距离栈顶的字节数 */</FONT></P>
<P><FONT face="Courier New" size=2>EBX = 0x00<BR>ECX = 0x04<BR>EDX =
0x08<BR>ESI = 0x0C<BR>EDI = 0x10<BR>EBP = 0x14<BR>EAX = 0x18<BR>DS =
0x1C<BR>ES = 0x20<BR>ORIG_EAX = 0x24<BR>EIP = 0x28<BR>CS = 0x2C<BR>EFLAGS =
0x30<BR>OLDESP = 0x34<BR>OLDSS = 0x38</FONT></P>
<P><FONT face="Courier New" size=2>/* 此Macro用来保存和设置所有相关寄存器*/</FONT></P>
<P><FONT face="Courier New" size=2>#define SAVE_ALL \<BR> cld;
\<BR> pushl %es; \<BR> pushl %ds; \<BR> pushl %eax;
\<BR> pushl %ebp; \<BR> pushl %edi; \<BR> pushl %esi;
\<BR> pushl %edx; \<BR> pushl %ecx; \<BR> pushl %ebx;
\<BR> movl $(__KERNEL_DS),%edx; \<BR> movl %edx,%ds; \<BR> movl
%edx,%es;</FONT></P>
<P><FONT face="Courier New" size=2>/* 为了便于理解,system_call中几条与此关键机制无关的指令被删去
*/</FONT></P>
<P><FONT face="Courier New" size=2>ENTRY(system_call)<BR> pushl
%eax # save orig_eax<BR> SAVE_ALL <BR> cmpl
$(NR_syscalls),%eax # %eax中存放的系统调用号必须为一个存在的号<BR> jae
badsys
# 否则返回-ENOSYS错误<BR> call
*SYMBOL_NAME(sys_call_table)(,%eax,4) # 以%eax为索引,调用相应系统调用函数<BR> movl
%eax,EAX(%esp) # save the return value</FONT></P>
<P><FONT face="Courier New" size=2>badsys:<BR> movl
$-ENOSYS,EAX(%esp)<BR> jmp ret_from_sys_call</FONT></P>
<P><FONT face="Courier New"
size=2>需要注意的是,由于函数参数传递的方式是将参数Push到Stack中,被调用函数执行时到Stack中访问相关参数,所以SAVE_ALL中将各个寄存器压栈的顺序是经过精心安排的,不能够随意更改。我们看栈顶的6个寄存器依次为%ebx,%ecx,%edx,%esi,%edi,%ebp,对于不需要参数的系统调用功能函数来说,它不需要访问任何保存在栈中的寄存器的值;对于需要1个参数的寄存器来说,栈顶%ebx寄存器的内容就是它所需的参数的值;对于需要2个参数的寄存器来说,栈顶的%ebx,%ecx寄存器的内容就是它所需的2个参数的值,其中%ebx对应第一个参数,%ecx对应第2个参数……依次类推,对于需要6个参数的寄存器来说,栈顶的6个寄存器的内容就依次是它所需的6个参数的值。</FONT></P>
<P><FONT face="Courier New"
size=2>另外,假如系统只提供了100个系统调用功能函数(0至99),而应用程序通过%eax寄存器传入的系统调用功能号为100以上的值,这很显然是一个非法调用,直接返回-ENOSYS错误。</FONT></P>
<P><FONT size=2><FONT size=+0><FONT face="Courier New"
size=2>当系统调用表(sys_call_table)中的某个系统调用功能函数被执行后,由于此函数运行在内核态,内核当然应该信任自身的任何代码,所以它可以访问任何资源,可以调用任何操作系统内核中的函数。此时我们认为用户应用程序运行在内核态。</FONT></P></FONT></FONT></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -