Chinaunix首页 | 论坛 | 博客
  • 博客访问: 914823
  • 博文数量: 119
  • 博客积分: 2493
  • 博客等级: 大尉
  • 技术积分: 2363
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-03 14:00
文章分类

全部博文(119)

文章存档

2013年(19)

2012年(100)

分类: LINUX

2012-06-16 18:07:06

1,初始化中断向量表。
   系统调用使用的是int 0x80号中断。当执行int 0x80后会执行中服务子程序。
   所执行的中断服务子程序就是系统调用的“主管”程序,system_call 
   函数所在的位置:arch/x86/kernel/entry_32.S
源代码如下:(2.6.39.2版本)
  1. 497 # system call handler stub
  2. 498 ENTRY(system_call)
  3. 499 RING0_INT_FRAME # can't unwind into user space anyway
  4. 500 pushl_cfi %eax # save orig_eax
  5. 501 SAVE_ALL
  6. 502 GET_THREAD_INFO(%ebp)
  7. 503 # system call tracing in operation / emulation
  8. 504 testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
  9. 505 jnz syscall_trace_entry
  10. 506 cmpl $(nr_syscalls), %eax
  11. 507 jae syscall_badsys
  12. 508 syscall_call:
  13. 509 call *sys_call_table(,%eax,4)
  14. 510 movl %eax,PT_EAX(%esp) # store the return value
  15. 511 syscall_exit:
  16. 512 LOCKDEP_SYS_EXIT
  17. 513 DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
  18. 514 # setting need_resched or sigpending
  19. 515 # between sampling and the iret
  20. 516 TRACE_IRQS_OFF
  21. 517 movl TI_flags(%ebp), %ecx
  22. 518 testl $_TIF_ALLWORK_MASK, %ecx # current->work
  23. 519 jne syscall_exit_work
------------------------------------------------------------------------
2,下面重点分析system_call函数的实现。
linux内核中定义了一个表,sys_call_table这个表实际上是一个函数指针数组,里面全部是函数指针,
形象点说就是钩子函数,系统调用的实现就是调用内核函数实现这些钩子函数。
-----------------------------
上面图的左边有一列数字,这就是“系统调用号”,定义的位置:arch/x86/include/asm/unistd_32.h
系统调用号实际上可以理解为系统调用的ID,每个系统调用都有个“名字”对其标识,每个“名字”的
背后都绑定着一个系统调用的ID。
-----------------------------------------------------------------------------------------
执行int 0x80时,由硬件完成一些寄存器的压栈。比如说,用户栈的栈地址(ss),栈顶地址(esp),状态寄存器,(eflags),程序计数器(cs:eip)
------------------------------------------------------------
  1. pushl_cfi %eax # save orig_eax
该条指令是对系统调用号留一个副本,以防需要的时候使用。
-----------------------------------------------------------
  1. SAVE_ALL
这实际上是汇编实现的一个宏。对部分寄存器进行了压栈,主要是为了保留系统调用的系统调用号,
和系统调用所传入的参数。具体对哪些寄存器入了栈可以查看下面的“注释二”中反汇编中的一系列
push操作。
-----------------------------------------------------------
  1. 506 cmpl $(nr_syscalls), %eax
  2. 507 jae syscall_badsys
上面是对系统调用号做安全性检查,因为在arch/x86/include/asm/unistd_32.h文件中
定义了一个系统调用的最大值,#define NR_syscalls  ***  如果系统调用号非法将不会
执行系统的服务例程,转而执行syscall_badsys作一系列善后工作返回一个出错码后退出。
------------------------------------------------------------
  1. call *sys_call_table(,%eax,4)
该语句的执行就是调用系统调用的服务例程(我们习惯把实现系统调用的内核函数称为系统调用的服务例程)。服务例程的地址所在的位置 = sys_call_table + eax * 4
eax 中是系统调用号。每个系统调用号占用4个字节。
-----------------------------------------------------------------------------------------
  1. 510 movl %eax,PT_EAX(%esp) # store the return value
执行完服务例程后返回的结果存放在eax中,这时候将返回的结果压栈到指定的位置。

注释一:
每个进程都会有两个栈,一个内核态栈和一个用户态栈。当执行int中断执行时就会由用户态栈转向内核栈
当执行iret时又会从内核态栈返回到用户态栈,一般来说,用户态栈的大小比较大,内核态栈的大小比较小。
--------------------------------------------------------------------------------------
注释二:使用objdump -d vmlinux反汇编system_call函数的汇编代码

  1. 1659088 c1508438 :
  2. 1659089 c1508438: 50 push %eax ------------------>将eax压栈
  3. 1659090 c1508439: fc cld
  4. ------------------------------------------------------------------------------------------
  5. 1659091 c150843a: 0f a8 push %gs
  6. 1659092 c150843c: 0f a0 push %fs
  7. 1659093 c150843e: 06 push %es
  8. 1659094 c150843f: 1e push %ds
  9. 1659095 c1508440: 50 push %eax ----------------------->SAVE_ALL
  10. 1659096 c1508441: 55 push %ebp
  11. 1659097 c1508442: 57 push %edi
  12. 1659098 c1508443: 56 push %esi
  13. 1659099 c1508444: 52 push %edx
  14. 1659100 c1508445: 51 push %ecx
  15. 1659101 c1508446: 53 push %ebx
  16. ------------------------------------------------------------------------------------------
  17. 1659102 c1508447: ba 7b 00 00 00 mov $0x7b,%edx
  18. 1659103 c150844c: 8e da mov %edx,%ds
  19. 1659104 c150844e: 8e c2 mov %edx,%es
  20. 1659105 c1508450: ba d8 00 00 00 mov $0xd8,%edx
  21. 1659106 c1508455: 8e e2 mov %edx,%fs ----------------------->GET_THREAD_INFO(%ebp)
  22. 1659107 c1508457: ba e0 00 00 00 mov $0xe0,%edx
  23. 1659108 c150845c: 8e ea mov %edx,%gs
  24. 1659109 c150845e: bd 00 e0 ff ff mov $0xffffe000,%ebp
  25. 1659110 c1508463: 21 e5 and %esp,%ebp
  26. ------------------------------------------------------------------------------------------
  27. 1659111 c1508465: f7 45 08 d1 01 00 10 testl $0x100001d1,0x8(%ebp)
  28. 1659112 c150846c: 0f 85 fe 00 00 00 jne c1508570
  29. 1659113 c1508472: 3d 59 01 00 00 cmp $0x159,%eax ------------->对系统调用号作有效性判断 (159)十六进制 = (345)十进制
  30. 1659114 c1508477: 0f 83 42 01 00 00 jae c15085bf ------------>如果你调用的系统号大于345则跳转到syscall_badsys
  31. 1659115
  32. 1659116 c150847d :
  33. 1659117 c150847d: ff 14 85 60 11 51 c1 call *-0x3eaeeea0(,%eax,4) ------>调用内核服务例程
  34. 1659118 c1508484: 89 44 24 18 mov %eax,0x18(%esp) ----------->将返回值存放在栈上。
  35. 1659119
  36. 1659120 c1508488 :
  37. 1659121 c1508488: 2e ff 15 e4 7b 72 c1 call *%cs:0xc1727be4
  38. 1659122 c150848f: 8b 4d 08 mov 0x8(%ebp),%ecx
  39. 1659123 c1508492: f7 c1 ff fe 00 10 test $0x1000feff,%ecx
  40. 1659124 c1508498: 0f 85 f2 00 00 00 jne c1508590
-----------------------------------------------------------------------------------------
注释三:

  1. 23 * 0(%esp) - %ebx
  2. 24 * 4(%esp) - %ecx
  3. 25 * 8(%esp) - %edx
  4. 26 * C(%esp) - %esi
  5. 27 * 10(%esp) - %edi
  6. 28 * 14(%esp) - %ebp
  7. 29 * 18(%esp) - %eax
  8. 30 * 1C(%esp) - %ds
  9. 31 * 20(%esp) - %es
  10. 32 * 24(%esp) - %fs
  11. 33 * 28(%esp) - %gs saved iff !CONFIG_X86_32_LAZY_GS
  12. 34 * 2C(%esp) - orig_eax
  13. 35 * 30(%esp) - %eip
  14. 36 * 34(%esp) - %cs
  15. 37 * 38(%esp) - %eflags
  16. 38 * 3C(%esp) - %oldesp
  17. 39 * 40(%esp) - %oldss
通过注释三可以查看寄存器压栈所在的位置。
阅读(6445) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~