Linux系统调用system_call
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的更多相关文章
- [Linux]系统调用理解(1)
本文是Linux系统调用专栏系列文章的第一篇,对Linux系统调用的定义.基本原理.使用方法和注意事项大概作了一个介绍,以便读者对Linux系统调用建立一个大致的印象. 什么是系统调用? Linux内 ...
- 关于Linux系统调用,内核函数【转】
转自:http://blog.csdn.net/ubuntulover/article/details/5988220 早上听人说到某个程序的一部分是内核态,另一部分是用户态,需要怎么怎么.当时突然想 ...
- Linux系统调用(转载)
目录: 1. Linux系统调用原理 2. 系统调用的实现 3. Linux系统调用分类及列表 4.系统调用.用户编程接口(API).系统命令和内核函数的关系 5. Linux系统调用实例 6. Li ...
- 剖析Linux系统调用的执行路径
在什么是操作系统这篇文章中,介绍过操作系统像是一个代理一样,为我们去管理计算机的众多硬件,我们需要计算机的一些计算服务.数据管理的服务,都由操作系统提供接口来完成.这样做的好处是让一般的计算机使用者不 ...
- linux内核剖析(六)Linux系统调用详解(实现机制分析)
本文介绍了系统调用的一些实现细节.首先分析了系统调用的意义,它们与库函数和应用程序接口(API)有怎样的关系.然后,我们考察了Linux内核如何实现系统调用,以及执行系统调用的连锁反应:陷入内核,传递 ...
- 使用 Linux 系统调用的内核命令【转】
转自:http://www.ibm.com/developerworks/cn/linux/l-system-calls/ 探究 SCI 并添加自己的调用 Linux® 系统调用 —— 我们每天都在使 ...
- Linux系统调用过程分析
參考: <Linux内核设计与实现> 0 摘要 linux的系统调用过程: 层次例如以下: 用户程序------>C库(即API):INT 0x80 ----->system_ ...
- [Linux]Linux系统调用列表
本文列出了大部分常见的Linux系统调用,并附有简要中文说明. 以下是Linux系统调用的一个列表,包含了大部分常用系统调用和由系统调用派生出的的函数.这可能是你在互联网上所能看到的唯一一篇中文注释的 ...
- 别出心裁的Linux系统调用学习法
别出心裁的Linux系统调用学习法 操作系统与系统调用 操作系统(Operating System,简称OS)是计算机中最重要的系统软件,是这样的一组系统程序的集成:这些系统程序在用户对计算机的使用中 ...
随机推荐
- 常用语句1【weber出品】
1.查看控制文件位置: select * from v$controlfile show parameter control; 2.查询日志文件位置 select group#,status,mem ...
- Something About Variable
CONTENT(目录) 前言 Variable declearation:three rules you can break 1.Don't set var stat ...
- tribonacci
Everybody knows Fibonacci numbers, now we are talking about the Tribonacci numbers: T[0] = T[1] = T[ ...
- pragram once
#pragma once [1]#pragma once这个宏有什么作用? 为了避免同一个文件被include多次,C/C++中有两种宏实现方式:一种是#ifndef方式,一种是#pragma o ...
- PreparedStatement可以有效地防止sql被注入
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import jav ...
- web安全:click jacking
点击劫持 click jacking通过iframe加载被攻击网站到黑客自己维护的网站通过z-index叠加和position定位,将2个网站的信息堆叠在一个立体投影面上,通过opacity设置透明 ...
- 电脑远程控制手机2—webkey
远程控制神器,这个是真真切切试验成功了.我用的手机是OPPOR817和华为A199. 网上去下载webkey,http://soft.shouji.com.cn/down/23169.html 然后安 ...
- hive 三种启动方式及用途,关注通过jdbc连接的启动
http://blog.csdn.net/a221133/article/details/6734746
- 关于Android读取不同位置(drawable,asset,SDCard)的图片资源的总结
方式一: 已将图片保存到drawable目录下,通过图片id获得Drawable或者Bitmap,此方式最常用.(若只知道图片的名称,还可以通过图片的名称获得图片的id) (1)通过图片id获得Dra ...
- java基本类型作为成员变量时的初始值
package primitivetypedefaultvalue; public class ListDefaultValue { public static void main(String[] ...