系统调用概述

计算机系统的各种硬件资源是有限的,在现代多任务操作系统上同一时候执行的多个进程都须要訪问这些资源,为了更好的管理这些资源进程是不同意直接操作的,全部对这些资源的訪问都必须有操作系统控制。也就是说操作系统是使用这些资源的唯一入口,而这个入口就是操作系统提供的系统调用(System Call)。在linux中系统调用是用户空间訪问内核的唯一手段,除异常和陷入外,他们是内核唯一的合法入口。

普通情况下应用程序通过应用编程接口API,而不是直接通过系统调用来编程。

在Unix世界,最流行的API是基于POSIX标准的。

操作系统通常是通过中断从用户态切换到内核态。中断就是一个硬件或软件请求,要求CPU暂停当前的工作,去处理更重要的事情。

比方。在x86机器上能够通过int指令进行软件中断。而在磁盘完毕读写操作后会向CPU发起硬件中断。

中断有两个重要的属性,中断号和中断处理程序。中断号用来标识不同的中断,不同的中断具有不同的中断处理程序。在操作系统内核中维护着一个中断向量表(Interrupt Vector Table)。这个数组存储了全部中断处理程序的地址,而中断号就是对应中断在中断向量表中的偏移量。

一般地,系统调用都是通过软件中断实现的,x86系统上的软件中断由int $0x80指令产生,而128号异常处理程序就是系统调用处理程序system_call()。它与硬件体系有关。在entry.S中用汇编写。

接下来就来看一下Linux下系统调用详细的实现过程。

Linux下系统调用的实现

前文已经提到了Linux下的系统调用是通过0x80实现的,可是我们知道操作系统会有多个系统调用(Linux下有319个系统调用),而对于同一个中断号是怎样处理多个不同的系统调用的?最简单的方式是对于不同的系统调用採用不同的中断号。可是中断号明显是一种稀缺资源,Linux显然不会这么做;另一个问题就是系统调用是须要提供參数,而且具有返回值的,这些參数又是怎么传递的?也就是说。对于系统调用我们要搞清楚两点:

1. 系统调用的函数名称转换。

2. 系统调用的參数传递。

首先看第一个问题。实际上,Linux中每一个系统调用都有对应的系统调用号作为唯一的标识,内核维护一张系统调用表,sys_call_table,表中的元素是系统调用函数的起始地址,而系统调用号就是系统调用在调用表的偏移量。在x86上,系统调用号是通过eax寄存器传递给内核的。比方fork()的实现:

在/usr/include/asm/unistd_32.h,能够通过find / -name unistd_32.h -print查找)

  1. #ifndef _ASM_X86_UNISTD_32_H
  2. #define _ASM_X86_UNISTD_32_H
  3. /*
  4. * This file contains the system call numbers.
  5. */
  6. #define __NR_restart_syscall      0
  7. #define __NR_exit                 1
  8. #define __NR_fork                 2
  9. #define __NR_read                 3
  10. #define __NR_write                4
  11. #define __NR_open                 5

所以详细调用fork的过程是:将2存入%eax中,然后进行系统调用,伪代码:

  1. mov     eax, 2
  2. int     0x80

对于參数传递,Linux是通过寄存器完毕的。Linux最多同意向系统调用传递6个參数,分别依次由%ebx,%ecx,%edx,%esi,%edi这5个寄存器完毕,须要6个及以上參数情况不多见。另外应该有一个单独的寄存器存放指向全部这些參数在用户空间的地址的指针。给用户空间的返回值通过eax寄存器传递。比方,调用exit(1),伪代码是:

  1. mov    eax, 2
  2. mov    ebx, 1
  3. int    0x80

由于exit须要一个參数1,所以这里仅仅须要使用ebx。这6个寄存器可能已经被使用,所以在传參前必须把当前寄存器的状态保存下来,待系统调用返回后再恢复,这个在后面栈切换再详细讲。

Linux中,在用户态和内核态执行的进程使用的栈是不同的。分别叫做用户栈和内核栈,两者各自负责对应特权级别状态下的函数调用。当进行系统调用时。进程不仅要从用户态切换到内核态。同一时候也要完毕栈切换。这样处于内核态的系统调用才干在内核栈上完毕调用。系统调用返回时,还要切换回用户栈,继续完毕用户态下的函数调用。

寄存器%esp(栈指针。指向栈顶)所在的内存空间叫做当前栈,比方%esp在用户空间则当前栈就是用户栈。否则是内核栈。栈切换主要就是%esp在用户空间和内核空间间的来回赋值。

在Linux中,每一个进程都有一个私有的内核栈,当从用户栈切换到内核栈时,需完毕保存%esp以及相关寄存器的值(%ebx。%ecx...)并将%esp设置成内核栈的对应值。

而从内核栈切换会用户栈时。须要恢复用户栈的%esp及相关寄存器的值以及保存内核栈的信息。一个问题就是用户栈的%esp和寄存器的值保存到什么地方,以便于恢复呢?答案就是内核栈,在调用int指令机型系统调用后会把用户栈的%esp的值及相关寄存器压入内核栈中。系统调用通过iret指令返回。在返回之前会从内核栈弹出用户栈的%esp和寄存器的状态。然后进行恢复。

相信大家一定听过说,系统调用非常耗时。要尽量少用。通过上面描写叙述系统调用的实现原理,大家也应该知道这当中的原因了。

第一,系统调用通过中断实现,须要完毕栈切换。

第二,使用寄存器传參,这须要额外的保存和恢复的过程。

上面关于系统调用的阐述,如有错误欢迎指正。。

Linux系统调用具体解释(怎样从用户空间进入内核空间)的更多相关文章

  1. Linux用户空间与内核空间(理解高端内存)

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  2. Linux用户空间与内核空间

    源:http://blog.csdn.net/f22jay/article/details/7925531 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针 ...

  3. linux 用户空间与内核空间——高端内存详解

    摘要:Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对 ...

  4. linux用户空间和内核空间(内核高端内存)_转

    转自:Linux用户空间与内核空间(理解高端内存) 参考: 1. 进程内核栈.用户栈 2. 解惑-Linux内核空间 3. linux kernel学习笔记-5 内存管理   Linux 操作系统和驱 ...

  5. Linux用户空间与内核空间(理解高端内存)【转】

    转自:http://www.cnblogs.com/wuchanming/p/4360277.html Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递 ...

  6. 在Linux用户空间做内核空间做的事情

    导读 我相信,Linux 最好也是最坏的事情,就是内核空间(kernel space)和用户空间(user space)之间的巨大差别.如果没有这个区别,Linux 可能也不会成为世界上影响力最大的操 ...

  7. Linux内存管理--用户空间和内核空间【转】

    本文转载自:http://blog.csdn.net/yusiguyuan/article/details/12045255 关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用 ...

  8. linux内存管理--用户空间和内核空间

    关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用户空间与内核空间.用户空间从0到3G(0xc0000000),内核空间占据3G到4G.用户进程通常情况下只能访问用户空间的虚拟 ...

  9. 如何看待Linux操作系统的用户空间和内核空间

    作为中央核心处理单元的CPU,除了生产工艺的不断革新进步外,在处理数据和响应速度方面也需要有权衡.稍有微机原理基础的人都知道Intel X86体系的CPU提供了四种特权模式ring0~ring3,其中 ...

随机推荐

  1. BZOJ 1303

    思路: 水题  竟然不会做 尴尬 比b大的数=1 比b小的数=-1 找到b 统计一下左边比b大x的数有多少 扫右边的时候就查左边的表 就可以了 //By SiriusRen #include < ...

  2. Hibernate多表映射(三)

    一对多|多对一 一个分类对应多个商品,一个商品只属于一个分类 创建分类表 products用set装,set特点值不能够重复 package com.hibernate.domain; import ...

  3. DOM高级编程

    前言:W3C规定的三类DOM标准接口(换图)Core DOM(核心DOM),适用于各种结构化文档:XML DOM(Java OOP学过),专用于XML文档:HTML DOM,专用于HTML文档,下面了 ...

  4. jq 禁用复选框 和输入框

    $('input').attr("readonly", ""); $('input').attr("disabled", "fal ...

  5. SQL基本概述

    DBMS的种类: 层次数据库HDB 面向对象数据库OODB XML数据库XMLDB 键值存储系统KVS 关系数据库RDB RDBMS(关系数据库管理系统),主要5有种: Oracle Database ...

  6. Vuex教程简单实例

    什么是Vuex? vuex是一个专门为vue.js设计的集中式状态管理架构.状态?我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态.简单的说就是data中需要共用的属性. ...

  7. Vue双向数据绑定实现原理

    https://zendq1998.github.io/2018/04/12/vue%E5%8F%8C%E5%90%91%E6%95%B0%E6%8D%AE%E7%BB%91%E5%AE%9A%E5% ...

  8. nyoj112-指数运算

    指数运算时间限制:600 ms  |  内存限制:65535 KB难度:2描述写一个程序实现指数运算 X^N.(1<X<10,0<N<20)输入输入包含多行数据 每行数据是两个 ...

  9. ubuntu 16.04 忘记登录密码的解决办法

    1.开机点击ESC或长按Shift,进入GUN GRUB界面  2.选择有recovery mode的选项,按e进入命令行  3.找到有recovery nomodeset的行,删除recovery ...

  10. 在eclipse中运行maven命令没有反应,console也不打印信息

    eclipse的maven项目中,在run as  执行maven命令的时候发现毫无反应,console也不打印信息,原因是因为没有传参数,解决办法如下:①打开eclipse的window菜单: ②接 ...