文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>linux 1.0 内核注解 linux/kernel/sys_call.S

linux 1.0 内核注解 linux/kernel/sys_call.S

时间:2009-03-03  来源:taozhijiangscu

/********************************************
 *Created By: 陶治江
 *Date:       2009-3-1
 ********************************************/
/* Stack layout in 'ret_from_system_call':
 * 可以看出的是:这里的系统调用是涉及到了特权级的转换的,
 * 因为从堆栈中可以分析出对原先的ss esp进行了保存了
 *  0(%esp) - %ebx
 *  4(%esp) - %ecx
 *  8(%esp) - %edx
 *   C(%esp) - %esi
 * 10(%esp) - %edi
 * 14(%esp) - %ebp
 * 18(%esp) - %eax
 * 1C(%esp) - %ds
 * 20(%esp) - %es
 *  24(%esp) - %fs
 * 28(%esp) - %gs
 * 2C(%esp) - orig_eax
 * 30(%esp) - %eip
 * 34(%esp) - %cs
 * 38(%esp) - %eflags
 * 3C(%esp) - %oldesp
 * 40(%esp) - %oldss
 */
#include <linux/segment.h> EBX  = 0x00
ECX  = 0x04
EDX  = 0x08
ESI  = 0x0C
EDI  = 0x10
EBP  = 0x14
EAX  = 0x18
DS  = 0x1C
ES  = 0x20
FS  = 0x24
GS  = 0x28
ORIG_EAX = 0x2C
EIP  = 0x30
CS  = 0x34
EFLAGS  = 0x38
OLDESP  = 0x3C
OLDSS  = 0x40
//这里是处理器EFLAGS中的各个标志的位图
CF_MASK  = 0x00000001
IF_MASK  = 0x00000200
NT_MASK  = 0x00004000
VM_MASK  = 0x00020000
/*these are offsets into the task-struct.(以字节为单位的偏移)
 *task_struct被定义在了linux/include/sched.h中,很长很长*/
state  =  0
counter  =  4
priority =  8
signal  = 12
blocked  = 16
flags  = 20
errno  = 24
dbgreg6  = 52
dbgreg7  = 56
ENOSYS = 38 //unsigned long intr_count = 0; .globl _system_call,_lcall7
.globl _device_not_available, _coprocessor_error
.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op
.globl _double_fault,_coprocessor_segment_overrun
.globl _invalid_TSS,_segment_not_present,_stack_segment
.globl _general_protection,_reserved
.globl _alignment_check,_page_fault
.globl ret_from_sys_call
 //对寄存器进行顺序保存,同时将ds es加载为内核的数据段
 //而fs设置为用户的数据段
#define SAVE_ALL \
 cld; \
 push %gs; \
 push %fs; \
 push %es; \
 push %ds; \
 pushl %eax; \
 pushl %ebp; \
 pushl %edi; \
 pushl %esi; \
 pushl %edx; \
 pushl %ecx; \
 pushl %ebx; \
      //相应的段寄存器的加载
 movl $(KERNEL_DS),%edx; \
 mov %dx,%ds; \
 mov %dx,%es; \
 movl $(USER_DS),%edx; \
 mov %dx,%fs;
#define RESTORE_ALL \
 cmpw $(KERNEL_CS),CS(%esp); \  //应该是查看当前是否是内核态
 je 1f;   \
 movl _current,%eax; \    //应该是当前任务
 movl dbgreg7(%eax),%ebx; \   //Hardware debugging registers的参数
 movl %ebx,%db7; \     //???
1: popl %ebx; \
 popl %ecx; \
 popl %edx; \
 popl %esi; \
 popl %edi; \
 popl %ebp; \
 popl %eax; \
 pop %ds; \
 pop %es; \
 pop %fs; \
 pop %gs; \
 addl $4,%esp; \
 iret   //这个是中断返回,会弹出堆栈中的eflags寄存器的
     //值,并同时弹出返回地址的
/*//struct pt_regs被定义在linux/include/linux/ptrace.h头文件中
struct pt_regs {
  long ebx;
  long ecx;
  long edx;
  long esi;
  long edi;
  long ebp;
  long eax;
  unsigned short ds, __dsu;
  unsigned short es, __esu;
  unsigned short fs, __fsu;
  unsigned short gs, __gsu;  <-  //SAVE_ALL宏可以保存的参数
  long orig_eax;
  long eip;
  unsigned short cs, __csu;
  long eflags;
  long esp;
  unsigned short ss, __ssu;
};*/
.align 4
_lcall7:
 pushfl   # We get a different stack layout with call gates,
 pushl %eax  # which has to be cleaned up later..
    //从堆栈的分布来看,这个pushl %eax可能就是通过RESTORE_ALL的
    //addl $4,%esp;来保存的,这个%eax就是pt_regs中的orig_eax了
 SAVE_ALL
 
 //下面的6步好像是对堆栈的数据进行调整
 //目前来看是可以了解的,因为在上面的SAVE_ALL调用之前是
 //push %eax,pushfl 而同上面的pt_regs结构来比较可以发现
 //至少应该是eip的位置变成了eflags
 //其他pt_regs的参数确定也只有在具体调用的地方确认了 :-(
 movl EIP(%esp),%eax  # due to call gates, this is eflags, not eip..
 movl CS(%esp),%edx  # this is eip..
 movl EFLAGS(%esp),%ecx # and this is cs..
 movl %eax,EFLAGS(%esp) #
 movl %edx,EIP(%esp)  # Now we move them to their "normal" places
 movl %ecx,CS(%esp)  #
 
 movl %esp,%eax   
 pushl %eax
 call _iABI_emulate  // linux/ibcs/emulate.c
 popl %eax    //至少现在的堆栈满足上面的条件的,但是pushl %eax
       //在call中什么用?
       //发现自己真的是混球了,%esp就是堆栈的地址啊,而
       //_iABI_emulate的参数就是堆栈指针啊 ~~~
 jmp ret_from_sys_call
.align 4
handle_bottom_half:
 pushfl
 incl _intr_count
 sti      //打开中断
 call _do_bottom_half
 popfl
 decl _intr_count
 jmp 9f
 
.align 4
reschedule:
 pushl $ret_from_sys_call
 jmp _schedule   //linux/kernel/sched.c,返回还是到ret_from_sys_call了
 
.align 4  //应该这里是所有的系统调用的入口点吧,起码这里
    //对中断向量的值进行了检测了
    //对,是的
_system_call:
 pushl %eax   # save orig_eax
 SAVE_ALL
 movl $-ENOSYS,EAX(%esp) //应该这些错误一般都是作为返回值的,下面也提到了
 cmpl _NR_syscalls,%eax //int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
       //定义的挺好的哈
 jae ret_from_sys_call
 
 movl _current,%ebx
 andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors
 
 //关于调试的真的不太懂哎
 movl $0,errno(%ebx)
 movl %db6,%edx
 movl %edx,dbgreg6(%ebx)  # save current hardware debugging status
 testb $0x20,flags(%ebx)  # PF_TRACESYS
 jne 1f
 
 call _sys_call_table(,%eax,4)
 movl %eax,EAX(%esp)  # save the return value
       //这里说了是返回值了,所以呢,前面是默认将错误的
       //返回值压入,这里正确了就进行覆盖
 movl errno(%ebx),%edx //0,如果成功的话
 negl %edx
 je ret_from_sys_call //这里表示正常,下面是异常,就将错误放在eax中返回,
       //同时设置CF标志
 movl %edx,EAX(%esp)  
 orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error
 jmp ret_from_sys_call
 
.align 4  //这里可能是调试调用的历程了
1: call _syscall_trace
 movl ORIG_EAX(%esp),%eax
 call _sys_call_table(,%eax,4)
 movl %eax,EAX(%esp)  # save the return value
 movl _current,%eax
 movl errno(%eax),%edx
 negl %edx
 je 1f
 movl %edx,EAX(%esp)
 orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error
1: call _syscall_trace

//现在感觉这是一个很重要重要的标号了!!!
 .align 4,0x90
ret_from_sys_call:
 cmpl $0,_intr_count
 jne 2f
   //这里的intr_count是0,然后检查是否还有需要
   //处理的中断,如果有就跳到handle_bottom_half处理
   //关于handle_bottom_half一次完整的执行是inc dec平衡的
   //如果intr_count==0就表示handle_bottom_half没有执行
   //就可以进行检测进行新的处理的
   //而handle_bottom_half的返回是直接返回到后面的9标号的
 movl _bh_mask,%eax
 andl _bh_active,%eax
 jne handle_bottom_half
 
9: movl EFLAGS(%esp),%eax  # check VM86 flag: CS/SS are
 testl $(VM_MASK),%eax  # different then
 jne 1f      //虚拟8086方式??
     
 cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ?
 je 2f
1: sti
 orl $(IF_MASK),%eax  # these just try to make sure
 andl $~NT_MASK,%eax  # the program doesn't do anything
 movl %eax,EFLAGS(%esp)  # stupid
 
 //下面就涉及到了任务的重新调度了
 
 cmpl $0,_need_resched //具体哪里设置这个标记得以后看了
 jne reschedule   
 
 movl _current,%eax
 cmpl _task,%eax   # task[0] cannot have signals
 je 2f     //task[0]是最悠闲的了
 
 //由于中断是不嵌套的,所以这里从中断返回的时候
 //如果当前的任务不是可执行的,或者时间被利用完
 //了,就需要进行重新调度了,否则如果没有信号要处理
 //就可以直接返回到中断进入的环境进行继续执行了
 
 cmpl $0,state(%eax)  # state TASK_RUNNING=0
 jne reschedule
 
 cmpl $0,counter(%eax) # counter //no time
 je reschedule
 movl blocked(%eax),%ecx
 movl %ecx,%ebx   # save blocked in %ebx for signal handling
 notl %ecx
 andl signal(%eax),%ecx //如果当前信号中除了可屏蔽的信号还有其他的信号
       //就进行do_signal的调用
 jne signal_return
2: RESTORE_ALL

.align 4
signal_return:
 movl %esp,%ecx
 pushl %ecx
 testl $(VM_MASK),EFLAGS(%ecx)
 
 jne v86_signal_return //当是虚拟8086的时候需要对部分数据进行保存操作
 
 pushl %ebx    //被屏蔽的信号量
 call _do_signal   //asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs);
       //调用参数是需要被屏蔽的信号量的
 popl %ebx
 popl %ebx    //平衡堆栈
 RESTORE_ALL
 
.align 4
v86_signal_return:
 call _save_v86_state   //linux/kernel/sys.c
         //上面的函数返回的是esp的值
 movl %eax,%esp
 pushl %eax      //这里作为第二个参数来给do_signal调用
 pushl %ebx
 call _do_signal
 popl %ebx
 popl %ebx
 RESTORE_ALL
//下面的基本都是先压入错误码,
//(感觉可能是如果调用函数没有压入错误码,这里就压入0占位,如果有
//错误码就会事先压入的,这里就不另外压入了)然后压入处理历程的函数,然后
//再调用error_code历程,这里全部是系统调用的实际操作了,
//从 error_code之后调用jmp ret_from_sys_call进行后续的处理
//对照看
/* Stack layout in 'ret_from_system_call':
 * 可以看出的是:这里的系统调用是涉及到了特权级的转换的,
 * 因为从堆栈中可以分析出对原先的ss esp进行了保存了
 *  0(%esp) - %ebx
 *  4(%esp) - %ecx
 *  8(%esp) - %edx
 *   C(%esp) - %esi
 * 10(%esp) - %edi
 * 14(%esp) - %ebp
 * 18(%esp) - %eax
 * 1C(%esp) - %ds
 * 20(%esp) - %es
 *  24(%esp) - %fs
 * 28(%esp) - %gs
 * 2C(%esp) - orig_eax
 * 30(%esp) - %eip
 * 34(%esp) - %cs
 * 38(%esp) - %eflags
 * 3C(%esp) - %oldesp
 * 40(%esp) - %oldss  */
.align 4
_divide_error:
 pushl $0  # no error code  ->%eax
 pushl $_do_divide_error    ->处理历程的地址,这个位置将会被后来的%gs所替换
.align 4,0x90
error_code:   //有错误号的处理方式
 push %fs
 push %es
 push %ds
 pushl %eax
 pushl %ebp
 pushl %edi
 pushl %esi
 pushl %edx
 pushl %ecx
 pushl %ebx
 
 movl $0,%eax
 movl %eax,%db7   # disable hardware debugging...
 cld
 
 movl $-1, %eax
 xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. )
 xorl %ebx,%ebx    # zero ebx
 mov %gs,%bx     # get the lower order bits of gs
 xchgl %ebx, GS(%esp)  # get the address and save gs.
 pushl %eax     # push the error code
        //虽然错误码压入的比较早,但是被交换到了比较近的地方了
 lea 4(%esp),%edx   
 pushl %edx
 
 //内核态
 movl $(KERNEL_DS),%edx
 mov %dx,%ds
 mov %dx,%es
 movl $(USER_DS),%edx
 mov %dx,%fs
 
 //这里可能是关于调试的部分,不是很懂
 pushl %eax
 movl _current,%eax
 movl %db6,%edx
 movl %edx,dbgreg6(%eax)  # save current hardware debugging status
 popl %eax
 
 call *%ebx     //交换出来的调用地址
 addl $8,%esp    //计算后堆栈平衡了
 jmp ret_from_sys_call
.align 4
_coprocessor_error:
 pushl $0
 pushl $_do_coprocessor_error
 jmp error_code
.align 4
//可能也是关于协处理器的
_device_not_available:
 pushl $-1  # mark this as an int
 SAVE_ALL
 pushl $ret_from_sys_call
 
 movl %cr0,%eax
 testl $0x4,%eax    # EM (math emulation bit)
 je _math_state_restore  //模拟协处理器,其实应该是如果协处理不存在
        //都会有这个调用的,但是如果设置了模拟位就跳出去了
        //否则就math_emulate,这里的实现就是崩溃了
 
 pushl $0  # temporary storage for ORIG_EIP
 call _math_emulate  //目前的实现是do_exit(),可能内核就傻掉了,所以不应该允许出现这个错误的
 addl $4,%esp
 
 ret    //注意这里不是iret,估计弹出的就是ret_from_sys_call了
.align 4
_debug:
 pushl $0
 pushl $_do_debug
 jmp error_code
.align 4
_nmi:
 pushl $0
 pushl $_do_nmi
 jmp error_code
.align 4
_int3:
 pushl $0
 pushl $_do_int3
 jmp error_code
.align 4
_overflow:
 pushl $0
 pushl $_do_overflow
 jmp error_code
.align 4
_bounds:
 pushl $0
 pushl $_do_bounds
 jmp error_code
.align 4
_invalid_op:
 pushl $0
 pushl $_do_invalid_op
 jmp error_code
.align 4
_coprocessor_segment_overrun:
 pushl $0
 pushl $_do_coprocessor_segment_overrun
 jmp error_code
.align 4
_reserved:
 pushl $0
 pushl $_do_reserved
 jmp error_code
.align 4
_double_fault:
 pushl $_do_double_fault
 jmp error_code
.align 4
_invalid_TSS:
 pushl $_do_invalid_TSS
 jmp error_code
.align 4
_segment_not_present:
 pushl $_do_segment_not_present
 jmp error_code
.align 4
_stack_segment:
 pushl $_do_stack_segment
 jmp error_code
.align 4
_general_protection:
 pushl $_do_general_protection
 jmp error_code
.align 4
_alignment_check:
 pushl $_do_alignment_check
 jmp error_code
.align 4
_page_fault:
 pushl $_do_page_fault
 jmp error_code
  文档地址:http://blogimg.chinaunix.net/blog/upfile2/090303173104.pdf
相关阅读 更多 +
排行榜 更多 +
爱是小事最新版

爱是小事最新版

休闲益智 下载
悬案2刹那惊颤游戏

悬案2刹那惊颤游戏

冒险解谜 下载
几何飞行内购修改版

几何飞行内购修改版

飞行射击 下载