进程的系统调用命令进行格式转换和参数传递要利用一个宏定义syscallN()(见include/asm/u
nistd.h).请看下例:
#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,\
type4,arg4,type5,arg5)\
type neme(type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5)\
{\
long __res;\
__asm__volatile("int $0x80" \
:"=a"(__res)\
:"0"(__NR_##name),"b"((long)(arg1)),"c"((long)(arg2)),\
"d"((long)(arg3)),"S"((long)(arg4)),"D"((long)(arg5)));\
if(__res>=0)\
return(type)__res;\
errno=-__res;\
return -1;\
}
通过这样的宏,进程的系统调用命令转换为INT 0x80中断请求.
N取0与5之间任意整数.参数个数为N的系统调用由syscallN负责格式转换和参数传递.例如,fork(),它对应的格式转换宏就是syscall0();open()有3个参数,它对应的格式转换宏就是syscall3(
).
syscallN()的第一个参数说明响应函数返回值的类型,第二个参数为系统调用的名称(即name),其
余的参数依次为系统调用参数的类型和名称.例如:
syscall3(int,open,const char *,file,int,flag,int,mode)
说明了系统调用命令:
int open(const char *file,int flag,int mode)
宏定义的余下部分描述了参数描述,启动INT
0x80以及接受,判断返回值的过程.也就是说,以系统调用号对EAX寄存器赋值,启动INT
0x80.规定返回值送EAX寄存器。函数的参数进行压栈,压栈顺序见下表:
----------------------------------------------------------------------
参数 参数在堆栈的位置 传递参数的寄存器
------------------------------------------------------------------------------
arg1 00(%esp) ebx
------------------------------------------------------------------------------
arg2 04(%esp) ecx
------------------------------------------------------------------------------
arg3 08(%esp) edx
------------------------------------------------------------------------------
arg4 0c(%esp) esi
------------------------------------------------------------------------------
arg5 10(%esp) edi
------------------------------------------------------------------------------
当执行INT 0x80时,以内核态进入入口地址system_call.
ENTRY(system_call)
pushl %eax
SAVE_ALL
:
:
SAVE_ALL是一个宏,目的是把CPU当前的寄存器的值压入堆栈(系统栈)
SAVE_ALL执行后系统栈的内容如下:
|---------------|
| SS |
|---------------|
| ESP |
|---------------|
| EFLAGS |
|---------------|
| CS |
|---------------|
| EIP |
|---------------|
|ORIG_EAX|
|---------------|
| ES |
|---------------|
| EAX |
|---------- ----|
| EBP |
|---------- ----|
| EDI |
|--------------|
| ESI |
|--------------|
| EDX |
|--------------|
| ECX |
|------------ -|
| EBX |
|------------ -|<---------堆栈指针
而内核中每一个处理系统调用的函数都以asmlinkage标志开头。这是为一些gcc功能定义的一个宏,
它告诉编译器该函数不希望从寄存器中取得任何参数,而希望仅仅从CPU堆栈中取得参数。也就是system_call后通过SAVE ALL被压入系统栈,内核处理函数直接从堆栈中取得这些参数.值得注意的是,Linux设置了一个称为
pt_regs的结构与系统栈中的内容相对应,内核处理函数通过该结构取得系统栈内相应参数的值.pt_
regs定义如下:
11544 /* this struct defines the way the registers are stored
11545 * on the stack during a system call. */
11546 struct pt_regs {
11547 long ebx;
11548 long ecx;
11549 long edx;
11550 long esi;
11551 long edi;
11552 long ebp;
11553 long eax;
11554 int xds;
11555 int xes;
11556 long orig_eax;
11557 long eip;
11558 int xcs;
11559 long eflags;
11560 long esp;
11561 int xss;
11562 };