2016-03-25

张超的《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

我的虚拟环境和代码在https://www.shiyanlou.com/courses/reports/1028332

我们这次主要分为两部分:

1.系统调用system_call的处理过程

2.给MenuOS增加time和time-asm命令

1.系统调用system_call的处理过程

490ENTRY(system_call)
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
# setting need_resched or sigpending
# between sampling and the iret
TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
testl $_TIF_ALLWORK_MASK, %ecx # current->work
jne syscall_exit_work
restore_all:
TRACE_IRQS_IRET
517restore_all_notrace:
#ifdef CONFIG_X86_ESPFIX32
movl PT_EFLAGS(%esp), %eax # mix EFLAGS, SS and CS
# Warning: PT_OLDSS(%esp) contains the wrong/random values if we
# are returning to the kernel.
# See comments in process.c:copy_thread() for details.
movb PT_OLDSS(%esp), %ah
movb PT_CS(%esp), %al
andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << ) | SEGMENT_RPL_MASK), %eax
cmpl $((SEGMENT_LDT << ) | USER_RPL), %eax
CFI_REMEMBER_STATE
je ldt_ss # returning to user-space with LDT SS
#endif
530restore_nocheck:
RESTORE_REGS # skip orig_eax/error_code
532irq_return:
INTERRUPT_RETURN
.section .fixup,"ax"
535ENTRY(iret_exc)
pushl $ # no error code
pushl $do_iret_error
jmp error_code
.previous
_ASM_EXTABLE(irq_return,iret_exc)

system_call

我们的system_call代码如上所述。

有实际开发经验的人都知道,在操作系统上运行的某个应用程序,如果想完成一些实际有用的功能,必然会用到操作系统提供的接口,这些接口被称为系统调用(System Call)。

由操作系统提供的功能,通常应用程序本身是无法实现的。例如对文件进行操作,应用程序必需通过系统调用才能做到,因为只有操作系统才具有直接管理外围设备的权限。又如进

程或线程间的同步互斥操作,也必需经由操作系统对内核变量进行维护才能完成。

应用程序的进程通常在user模式下运行,当它调用一个系统调用时,进程进入kernel模式,执行的是kernel内部的代码,从而具有执行特权指令的权限,完成特定的功能。换句话说,

系统调用是应用程序主动进入操作系统内核的入口。

由程序员的代码主动发起的中断。有两种用法:(1)用来实现系统调用;(2)通知调试器某个特殊事件。

至此,我们发现了中断与系统调用的关系:系统调用是一种特殊的中断类型。

系统调用的处理例程在IDT表中占有一项。这一项是在trap_init函数中被初始化的,如下:

set_system_gate(SYSCALL_VECTOR,&system_call);

当系统调用发生时,通过中断机制,系统调用例程system_call被调用。system_call由汇编语言和C的代码构成,它的执行过程大概分为4个步骤(注意参数的传入和返回值的传出过

程):

从寄存器中取出系统调用号(system call number)和输入参数,然后将这些寄存器的值压入kernel栈中。这一部分的代码用汇编写成。

根据系统调用号(system call number)查找系统调用分派表(system call dispatch table),找到系统调用服务例程(system call service routine )。汇编语言。

调用查到的系统调用服务例程。这一部分用C语言写成,因为已经将输入参数保存在kernel栈中,所以在C函数的参数表中能够拿到输入参数,使得系统调用服务例程在表面上

看与一个普通的C函数没有区别。

将系统调用服务例程的返回值出栈,重新保存在寄存器中。汇编语言。

上面描述的系统调用例程system_call在kernel空间中执行。在执行前,系统调用号和输入参数已经存入了寄存器,这个存入过程由user空间的代码完成。实际上,每个真正的系统调

用基本上都有一个封装它的库函数,一般是在这个库函数中完成系统调用号和输入参数的保存动作。当系统调用例程system_call执行完毕后,返回值通过寄存器再传回user空间的库

函数。

下面详细地介绍上面所讲的4个步骤。

在第1步之前,user空间的封装函数已经将对应的系统调用号保存在eax寄存器中,将输入参数保存在ebx, ecx, edx, esi,以及edi寄存器中(因此最多传6个参数,包括系统调用号)。

第1步中将输入参数寄存器的值压入kernel栈的操作由汇编代码__SAVE_ALL宏完成。如下:

#define __SAVE_ALL \

cld; \

pushl %es; \

pushl %ds; \

pushl %eax; \

pushl %ebp; \

pushl %edi; \

pushl %esi; \

pushl %edx; \

pushl %ecx; \

pushl %ebx; \

movl $(__USER_DS), %edx; \

movl %edx, %ds; \

movl %edx, %es;

第2步中的系统分派表在kernel代码中以变量sys_call_table表示。查找系统调用服务例程的动作就是从sys_call_table里找系统调用号(存在eax寄存器中)指向的那一项,如下:

syscall_call:

call *sys_call_table(,%eax,4)

sys_call_table中的项在sys_call_table.c文件中定义:

syscall_handler_t *sys_call_table[] = {

......

[ __NR_exit ] (syscall_handler_t *) sys_exit,

[ __NR_fork ] (syscall_handler_t *) sys_fork,

[ __NR_read ] = (syscall_handler_t *) sys_read,

[ __NR_write ] = (syscall_handler_t *) sys_write,

......

[ __NR_socketcall ] (syscall_handler_t *) sys_socketcall,

......

};

在这里我们注意到一些常用的系统调用号,如,exit系统调用号为__NR_exit = 1,fork系统调用号为__NR_fork =2,read系统调用号为__NR_read = 3,write系统调用号为

__NR_write =4,所有socket相关的API的系统调用号都是__NR_socketcall= 102。

第3步,执行C函数实现的系统调用例程。该例程最多接受6个参数(包括系统调用号),返回值是一个整型。返回值为非负,表示执行成功;返回值为负,表示执行出错,该错误码的

绝对值会最后存在user空间的errno全局变量中。

第4步,调用syscall_exit_work退出系统调用,并从kernel模式回到user模式。第3步的C函数执行return err的时候,编译后的代码已经将返回值存在了eax寄存器中。

最后,回到user模式的封装函数中,对返回值eax进行检查。如果eax小于0,则将eax的相反数(即绝对值)存到errno全局变量中,同时将eax值置为-1,这时封装函数返回-1;如果

eax大于等于0,则封装函数返回eax的值。

具体分析:

大致过程:SYSterm_call---运行到SAVE—ALL(保护现场)继续运行----table表找对应程序----iret结束返回

分析call对应函数功能(简化)

注意)通过SAVE_ALL宏完成把所有相关寄存器的内容都保存在堆栈中

以下是各项简化函数的表示意思,依据视频依次是:

GET_THREAD_INFO(%ebp):将当前信息保存在ebp

testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%esp)  jnz syscall_trace_entry:判断是否 trace调用

cmpl $(NR_syscalls), %eax jae syscall_badsys:判断系统调用号是否超出最大值

call *sys_call_table(,%eax,4):系统调用的数字实际上是一个序列号,表示其在系统的一个数组sys_call_table[]中的位置。

movl %eax,PT_EAX(%esp):保存系统调用的返回值

DISABLE_INTERRUPTS(CLBR_ANY):屏蔽其他系统调用

movl TI_flags(%esp), %eax:寄存器ecx是通用寄存器,在保护模式中,可以作为内存偏移指针(此时,DS作为 寄存器或段选择器),此时为返回到系统调用之前做准备

testl $_TIF_ALLWORK_MASK, %eax     jne syscall_exit_work :退出系统调用之前,检查是否需要处理信号

RESTORE_REGS 4​ :x86架构恢复寄存器代码

INTERRUPT_RETURN即iret: 系统调用是通过软中断指令

INT 0x80 实现的,而这条INT 0x80指令就被封装在C库的函数中。(软中断和我们常说的硬中断不同之处在于,软中断是由指令触发的,而不是由硬件外设引起的。)

INT 0x80 这条指令的执行会让系统跳转到一个预设的内核空间地址,它指向系统调用处理程序,即system_call函数。

实验过程如下:

1.先切换到我们的虚拟机的LinuxKernel目录下

2.qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

3.右键->新建窗口(水平分割)-> gdb

4.file linux-3.18.6/vmlinux

5.target remote:1234

6.b start_kernel

7.c

2.给MenuOS增加time和time-asm命令

0)更新menu代码到最新版

1)在main函数中增加MenuConfig

2)增加对应的Time函数和TimeASM函数

3)make rootfs

具体如下:

Linux系统调用system_call的更多相关文章

  1. [Linux]系统调用理解(1)

    本文是Linux系统调用专栏系列文章的第一篇,对Linux系统调用的定义.基本原理.使用方法和注意事项大概作了一个介绍,以便读者对Linux系统调用建立一个大致的印象. 什么是系统调用? Linux内 ...

  2. 关于Linux系统调用,内核函数【转】

    转自:http://blog.csdn.net/ubuntulover/article/details/5988220 早上听人说到某个程序的一部分是内核态,另一部分是用户态,需要怎么怎么.当时突然想 ...

  3. Linux系统调用(转载)

    目录: 1. Linux系统调用原理 2. 系统调用的实现 3. Linux系统调用分类及列表 4.系统调用.用户编程接口(API).系统命令和内核函数的关系 5. Linux系统调用实例 6. Li ...

  4. 剖析Linux系统调用的执行路径

    在什么是操作系统这篇文章中,介绍过操作系统像是一个代理一样,为我们去管理计算机的众多硬件,我们需要计算机的一些计算服务.数据管理的服务,都由操作系统提供接口来完成.这样做的好处是让一般的计算机使用者不 ...

  5. linux内核剖析(六)Linux系统调用详解(实现机制分析)

    本文介绍了系统调用的一些实现细节.首先分析了系统调用的意义,它们与库函数和应用程序接口(API)有怎样的关系.然后,我们考察了Linux内核如何实现系统调用,以及执行系统调用的连锁反应:陷入内核,传递 ...

  6. 使用 Linux 系统调用的内核命令【转】

    转自:http://www.ibm.com/developerworks/cn/linux/l-system-calls/ 探究 SCI 并添加自己的调用 Linux® 系统调用 —— 我们每天都在使 ...

  7. Linux系统调用过程分析

    參考: <Linux内核设计与实现> 0 摘要 linux的系统调用过程: 层次例如以下: 用户程序------>C库(即API):INT 0x80 ----->system_ ...

  8. [Linux]Linux系统调用列表

    本文列出了大部分常见的Linux系统调用,并附有简要中文说明. 以下是Linux系统调用的一个列表,包含了大部分常用系统调用和由系统调用派生出的的函数.这可能是你在互联网上所能看到的唯一一篇中文注释的 ...

  9. 别出心裁的Linux系统调用学习法

    别出心裁的Linux系统调用学习法 操作系统与系统调用 操作系统(Operating System,简称OS)是计算机中最重要的系统软件,是这样的一组系统程序的集成:这些系统程序在用户对计算机的使用中 ...

随机推荐

  1. UI中的七种手势

    // // GestureRecognizerViewController.m #import "GestureRecognizerViewController.h" #impor ...

  2. 1202.2——Xcode部分快捷键

    Ctrl+N(Next)  光标跳到下一行 Ctrl+P(Previous)   光标跳到上一行 Ctrl+B(Back) 光标向左边移动一个字符 Ctrl+F(Forward)   光标向右边移动一 ...

  3. WebBasic-表单

    用来提交数据 <form></form> 属性:action:提交的url  method:表单数据提交的方式  enctype:表单数据的编码方式 表单控件 --input元 ...

  4. HTML5+Css3-webkit-filter

    -webkit-filter 现在规范中支持的效果有: grayscale 灰度 sepia 褐色 saturate 饱和度 hue-rotate 色相旋转 invert 反色 opacity 透明度 ...

  5. js 比较日期大小

    //1获取当前时间 var curTime = new Date(); //2把字符串格式转换为日期类 var startTime = new Date(Date.parse(kc.begintime ...

  6. cf C. Maze

    http://codeforces.com/problemset/problem/378/C #include <cstdio> #include <cstring> #inc ...

  7. vbox端口转发

    端口转发:setting->network->adapter:attached to NAT.port forwarding rules->name    protocol     ...

  8. 安卓,分享到facebook的若干种方法汇总

    近期做了facebook的分享功能,遇到了很多问题,这里总结一下,供大家参考,不足之处还请大家指正. facebook分享方式: 1.通过intent调用调用本地facebook应用方式 支持单独分享 ...

  9. js 数组引用 发现的问题

    最近做项目时,要对返回的数据[保存在json数组中]做一次修改,但原数据要保留一次做备用.首先想到,原数据不动,用一个临时的变量来修改,大致模型就是这样: // 原始: a=[1,2,3,4,5,.. ...

  10. 精美实用的jQuery插件精选

    jQuery的确是一款相当强大的Javascript框架,同时jQuery的插件就多入牛毛,如果你善于收集,那么你在写前端页面的时候肯定会更加方便.本文精选了一些精美实用的jQuery插件供大家参考. ...