系统调用小识

:xujianguo

原创作品转载请注明出处,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

——————————————————————————————————————————————————————-————

实验目的:

       通过gdb调试和汇编嵌入menuOS的文件来进一步熟悉linux系统调用。

实验环境:

  

        实验楼:www.shiyanlou.com。

实验步骤:

1.配置环境,登录实验楼网站。

  按照上次实验的基本步骤,结合老师视频所讲,完成相关实验。

cd LinuxKernel

删除menu

然后从github上克隆相应的mengning/menu.git

2.编译menu,生成rootfs。

3.调整main.c,添加上次实验所用的系统调用段。

系统调用:

MenuConfig(“open”,"open or create file", Open);

汇编嵌入也类似。

4.编译和配置调试条件。

按照学过的方式配置环境:

5.启动gdb调试器,分别在sys_open、start_kernel、system_call处设置断点调试。

实验分析:

  实验参考资料:

     http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/syscalls/syscall_32.tbl

   http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/kernel/entry_32.S

重要源码分析:

# system call handler stub

490ENTRY(system_call) //系统调用入口

491 RING0_INT_FRAME # can't unwind into user space anyway

492 ASM_CLAC

493 pushl_cfi %eax # save orig_eax

494 SAVE_ALL //保存现场

495 GET_THREAD_INFO(%ebp)

496 # system call tracing in operation / emulation

497 testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)

498 jnz syscall_trace_entry

499 cmpl $(NR_syscalls), %eax

500 jae syscall_badsys

501syscall_call: //调用系统调用号

502 call *sys_call_table(,%eax,4)

503syscall_after_call: //调用结束的处理。

504 movl %eax,PT_EAX(%esp) # store the return value

505syscall_exit: //退出调用

506 LOCKDEP_SYS_EXIT

507 DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt

508 # setting need_resched or sigpending

509 # between sampling and the iret

510 TRACE_IRQS_OFF

511 movl TI_flags(%ebp), %ecx

512 testl $_TIF_ALLWORK_MASK, %ecx # current->work

513 jne syscall_exit_work

514 515restore_all:

516 TRACE_IRQS_IRET

517restore_all_notrace:

..........

530restore_nocheck:

531 RESTORE_REGS 4 # skip orig_eax/error_code

532irq_return://系统调用返回

533 INTERRUPT_RETURN

534.section .fixup,"ax"

535ENTRY(iret_exc)

536 pushl $0 # no error code

537 pushl $do_iret_error

538 jmp error_code

...............

588ENDPROC(system_call)

589

590 # perform work that needs to be done immediately before resumption

591 ALIGN

592 RING0_PTREGS_FRAME # can't unwind into user space anyway

593work_pending:

594 testb $_TIF_NEED_RESCHED, %cl

595 jz work_notifysig

596work_resched:

 655 ALIGN

656syscall_exit_work://系统调用结束处理环节

657 testl $_TIF_WORK_SYSCALL_EXIT, %ecx

658 jz work_pending

659 TRACE_IRQS_ON

660 ENABLE_INTERRUPTS(CLBR_ANY) # could let syscall_trace_leave() call

661 # schedule() instead

662 movl %esp, %eax

663 call syscall_trace_leave

664 jmp resume_userspace

665END(syscall_exit_work)

666 CFI_ENDPROC

 668 RING0_INT_FRAME # can't unwind into user space anyway

669syscall_fault://默认处理

670 ASM_CLAC

671 GET_THREAD_INFO(%ebp)

672 movl $-EFAULT,PT_EAX(%esp)

673 jmp resume_userspace

674END(syscall_fault)

675

676syscall_badsys://坏调用处理

677 movl $-ENOSYS,%eax

678 jmp syscall_after_call

679END(syscall_badsys)

680

....................

  

      本次实验是基于上次实验的基础进一步分析和发现,通过编译系统调试的方式来分析system_call。实验过程中,发现所使用open函数如果在启动过程中设置断点的话,调试的步骤比较多,只好跳过;在用高蛋白调试的过程中,n(Next)和s(Step)两种命令的区别和联系,值得注意;其中主要是在sys_open 、sys_time和system_call等处设置测试点。由于个人知识储备不足,对系统调用的部分细节没有好的意见,敬请谅解。

      下面继续对system_call对应的汇编代码的工作过程进行分析:

      系统在用户态和核心态之间的系统调用主要是通过int指令来完成的。start_kernel中trap_init()完成系统调用的初始化。

根据entry_32.S(简略细节如下图)文件的system_call环节可知,

1)ENTRY(system_call) //入口,标志着系统已经由用户态转向Kernel;

2)SAVE_ALL//保存相关寄存器的值,起着保护现场的作用,方便中断返回;

3)GET_THREAD_INFO(%ebp) // 通常ebp寄存器被高级语言编译器用以建造‘堆栈帧’来保存函数或过程的局部变量,此时可以知道将来返回到那个函数的那个部位
4)syscall_call: //真正的系统调用!sys_call_table就是
call *sys_call_table(,%eax,4)
movl %eax,PT_EAX(%esp) //保存系统调用的返回值
5)syscall_exit:
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_ANY) //屏蔽其他系统调用
..............
6)restore_all_notrace:
........
je ldt_ss //返回到用户空间,系统调用返回

7)RESTORE_ALL//恢复现场;

8)irq_return//返回调用

总结:

      系统调用主要是用户态向核心态进行调用请求,主要以中断的形式完成调度。在核心态下,系统调用通过系统调用号来完成调用匹配,system_call进行调度,sys_xxx来完成相应的系统调用,ret_from_sys_call获取返回结果,进行相关处理和调用后,通过iret返回相关结果。

     系统调用实际上是利用了中断的处理过程。而中断的一般处理过程是:当CPU收到中断或者异常的信号时,它会暂停执行当前的程序或任务,通过一定的机制跳转到负责处理这个信号的相关处理程序中,在完成对这个信号的处理后再跳回到刚才被打断的程序或任务中。

     在LINUX中,发生中断时,内核就在被中断进程的上下文中,在内核态下执行中断服务例程。但同时会保留所有需要用到的资源,以便中继服务结束时能恢复被中断进程的执行。

参考资料:

 1. 老师的讲解视频;

 2.http://www.tuicool.com/articles/NNFzuu

 3.http://blog.csdn.net/wangzhen199009/article/details/38677075

原文地址:https://www.cnblogs.com/emochuanshuo/p/4394972.html