nullChapter 5: 系统调用Chapter 5: 系统调用为什么需要系统调用
相关数据和代码
例:系统调用getuid()的实现
添加一个系统调用mysyscall
再实现一个稍复杂的系统调用
为什么需要系统调用(1)为什么需要系统调用(1)为什么需要系统调用(2)为什么需要系统调用(2)相关数据和代码相关数据和代码arch/i386/kernel/traps.c
arch/i386/kernel/entry.S
系统调用时的内核栈
sys_call_table
system_call和ret_from_sys_call
include/linux/unistd.h
系统调用编号
宏定义展开系统调用
glibc展开系统调用INLINE_SYSCALL (getuid, 0);
系统调用时的内核栈系统调用时的内核栈陷入内核时,系统自动从当前进程的TSS(任务状态段)中获得内核栈的SS和ESP,并完成栈切换系统调用时的内核栈系统调用时的内核栈18 * Stack layout in 'ret_from_system_call':
19 * ptrace needs to have all regs on the stack.
20 * if the order here is changed, it needs to be
21 * updated in fork.c:copy_process, signal.c:do_signal,
22 * ptrace.c and ptrace.h
23 *
24 * 0(%esp) - %ebx
25 * 4(%esp) - %ecx
26 * 8(%esp) - %edx
27 * C(%esp) - %esi
28 * 10(%esp) - %edi
29 * 14(%esp) - %ebp
30 * 18(%esp) - %eax
31 * 1C(%esp) - %ds
32 * 20(%esp) - %es
33 * 24(%esp) - orig_eax
34 * 28(%esp) - %eip
35 * 2C(%esp) - %cs
36 * 30(%esp) - %eflags
37 * 34(%esp) - %oldesp
38 * 38(%esp) - %oldss
39 *
40 * "current" is in register %ebx during any slow entries. 系统调用时的内核栈系统调用时的内核栈#define SAVE_ALL \ cld; \ pushl %es; \ pushl %ds; \ pushl %eax; \ pushl %ebp; \ pushl %edi; \ pushl %esi; \ pushl %edx; \ pushl %ecx; \ pushl %ebx; \ movl $(__KERNEL_DS),%edx; \ movl %edx,%ds; \ movl %edx,%es;
系统调用时的内核栈系统调用时的内核栈#define RESTORE_ALL \ popl %ebx; \ popl %ecx; \ popl %edx; \ popl %esi; \ popl %edi; \ popl %ebp; \ popl %eax; \ 1: popl %ds; \ 2: popl %es; \ addl $4,%esp; \ 3: iret; \ sys_call_tablesys_call_table398 ENTRY(sys_call_table)
399 .long SYMBOL_NAME(sys_ni_syscall)
400 .long SYMBOL_NAME(sys_exit)
401 .long SYMBOL_NAME(sys_fork)
402 .long SYMBOL_NAME(sys_read)
403 .long SYMBOL_NAME(sys_write)
404 .long SYMBOL_NAME(sys_open) /* 5 */
…
635 .long SYMBOL_NAME(sys_ni_syscall) /* reserved for lremovexattr */
636 .long SYMBOL_NAME(sys_ni_syscall) /* reserved for fremovexattr */
637
638 .rept NR_syscalls-(.-sys_call_table)/4
639 .long SYMBOL_NAME(sys_ni_syscall)
640 .endr sys_call_tablesys_call_tablesystem_callsystem_call194 ENTRY(system_call) # int 0x80后执行
195 pushl %eax # save orig_eax
196 SAVE_ALL
197 GET_CURRENT(%ebx)
198 testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS
199 jne tracesys
200 cmpl $(NR_syscalls),%eax
201 jae badsys
202 call *SYMBOL_NAME(sys_call_table)(,%eax,4)
203 movl %eax,EAX(%esp) # save the return value
204 ENTRY(ret_from_sys_call)
205 cliret_from_sys_callret_from_sys_callENTRY(ret_from_sys_call) cli # need_resched and signals atomic test cmpl $0,need_resched(%ebx) jne reschedule cmpl $0,sigpending(%ebx) jne signal_return restore_all: RESTORE_ALL
nullarch/i386/kernel/traps.carch/i386/kernel/traps.c916 void __init trap_init(void) //初始化中断描述符
IDT
{
……
923 set_trap_gate(0,÷_error);
924 set_trap_gate(1,&debug);
925 set_intr_gate(2,&nmi);
926 set_system_gate(3,&int3);
……
942 set_trap_gate(19,&simd_coprocessor_error);
944 set_system_gate(SYSCALL_VECTOR,&system_call); 系统调用编号系统调用编号include/asm-i386/unistd.h
8 #define __NR_exit 1
9 #define __NR_fork 2
10 #define _NR_read 3
11 #define NR_write 4
12 #define NR_open 5
13 #define NR_close 6
……
240 #define __NR_llistxattr 233
241 #define __NR_flistxattr 234
242 #define __NR_removexattr 235
243 #define __NR_lremovexattr 236
244 #define __NR_fremovexattr 237宏定义展开系统调用宏定义展开系统调用#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ type name(type1 arg1,type2 arg2,type3 arg3) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \ "d" ((long)(arg3))); \ __syscall_return(type,__res); \ }
宏定义展开系统调用宏定义展开系统调用movl $__NR_##name, %eax //先为输入参数分配寄存器
movl arg1, %ebx
movl arg2, %ecx
movl arg3, %edx
#APP
int $0x80 //汇编代码
#NO_APP
movl %eax, __res //最后处理输出参数 例:系统调用getuid()的实现例:系统调用getuid()的实现一个简单的程序,但包含系统调用和库函数调用
#include
/* all system calls need this header */
int main(){
int i = getuid();
printf(“Hello World! This is my uid: %d\n”, i);
}
假定定义了“宏”
_syscall0( int, getuid);
例:系统调用getuid()的实现例:系统调用getuid()的实现这个“宏”被getuid()展开后
int getuid(void)
{
long __res;
__asm__ volatile ("int $0x80"
: “=a” (__res) #输出
: “” (__NR_getuid)); #输入
__syscall_return(int,__res);
}
显然,__NR_getuid(24)放入eax,并int 0x80例:系统调用getuid()的实现例:系统调用getuid()的实现因为系统初始化时设定了
set_system_gate(SYSCALL_VECTOR,&system_call);
所以进入system_call
194 ENTRY(system_call)
195 pushl %eax # save orig_eax
196 SAVE_ALL
197 GET_CURRENT(%ebx)
198 testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS
199 jne tracesys
200 cmpl $(NR_syscalls),%eax
201 jae badsys
202 call *SYMBOL_NAME(sys_call_table)(,%eax,4)
203 movl %eax,EAX(%esp) 例:系统调用getuid()的实现例:系统调用getuid()的实现注意第202行,此时eax为24
查sys_call_table,得到call指令的操作数sys_getuid16
调用函数sys_getuid16()
145 asmlinkage long sys_getuid16(void)
146 {
147 return high2lowuid(current->uid);
148 } 例:系统调用getuid()的实现例:系统调用getuid()的实现第202行完成后,接着执行第203行后面的指令
203 movl %eax,EAX(%esp)
204 ENTRY(ret_from_sys_call)
205 cli
206 cmpl $0,need_resched(%ebx)
207 jne reschedule
208 cmpl $0,sigpending(%ebx)
209 jne signal_return
210 restore_all:
211 RESTORE_ALL例:系统调用getuid()的实现例:系统调用getuid()的实现第203行:返回值从eax移到堆栈中eax的位置
假设没有什么意外发生,于是ret_from_sys_call直接到RESTORE_ALL,从堆栈里面弹出保存的寄存器,堆栈切换(iret)
进程回到用户态,返回值保存在eax中
printf打印出
Hello World! This is my uid:551添加一个系统调用mysyscall添加一个系统调用mysyscall功能要求
调用这个mysyscall ,使用户的uid等于0
添加一个系统调用mysyscall添加一个系统调用mysyscall系统调用的编号名字 __NR_mysyscall
改写/usr/include/asm/unistd.h
240 #define __NR_llistxattr 233
241 #define __NR_flistxattr 234
242 #define __NR_removexattr 235
243 #define __NR_lremovexattr 236
244 #define __NR_fremovexattr 237
245 #define __NR_mysyscall 238添加一个系统调用mysyscall添加一个系统调用mysyscall内核中实现该系统调用的程序的名字 sys_mysyscall
改写arch/i386/kernel/entry.S
398 ENTRY(sys_call_table)
399 .long SYMBOL_NAME(sys_ni_syscall)
……
636 .long SYMBOL_NAME(sys_ni_syscall)
637 .long SYMBOL_NAME(sys_mysyscall)
638
639 .rept NR_syscalls-(.-sys_call_table)/4
640 .long SYMBOL_NAME(sys_ni_syscall)
641 .endr 添加一个系统调用mysyscall添加一个系统调用mysyscall把一小段程序添加在kernel/sys.c
asmlinkage int sys_mysyscall(void)
{
current->uid = current->euid = current->suid = current->fsuid = 0;
return 0;
}
记得重新编译内核,启动新内核添加一个系统调用mysyscall添加一个系统调用mysyscall编写一段测试程序检验实验结果
#include
_syscall0(int,mysyscall) /* 注意这里没有分号 */
int main()
{
mysyscall();
printf(“em…, this is my uid: %d. \n”, getuid());
} 一个稍复杂的系统调用一个稍复杂的系统调用用到了module
5-6-3.c:sys_pedagogictime(struct timeval *tv)
5-6-4.c:测试pedagogictime