内核版本:Linux-4.19

操作系统通过系统调用为运行于其上的进程提供服务。

那么,在应用程序内,调用一个系统调用的流程是怎样的呢?

我们以一个假设的系统调用 xyz() 为例,介绍一次系统调用的所有环节。

如上图所示,系统调用执行的流程如下:

1. 应用程序代码调用 xyz(),该函数是一个包装系统调用的库函数;
2. 库函数 xyz() 负责准备向内核传递的参数,并触发软中断以切换到内核;
3. CPU 被软中断打断后,执行中断处理函数,即系统调用处理函数(system_call);
4. 系统调用处理函数调用系统调用服务例程(sys_xyz ),真正开始处理该系统调用。

系统调用的实现来自于Glibc,几乎所有 C 程序都要调用 Glibc 的动态链接库 libc.so 中的库函数。这些库函数的源码是不可见的,可通过 objdump 或 gdb 等工具对代码进行汇编反编译,摸清大体的过程。

我们可不必太过纠结,知道原理就好。

下面继续分析在内核中的实现过程。

Pure EABI user space always put syscall number into scno (r7).

当从用户态转为内核态时,系统会将 syscall number 存储在寄存器 R7 中,利用 R7 来传参。

在 entry-header.S 文件中,有如下代码:

scno	.req	r7		@ syscall number
tbl .req r8 @ syscall table pointer
why .req r8 @ Linux syscall (!= 0)
tsk .req r9 @ current thread_info

类似于给寄存器起了个“别名”。

最后通过

invoke_syscall tbl, scno, r10, __ret_fast_syscall

代码成功调用 syscall table 中的服务程序。

invoke_syscall 定义如下:

	.macro	invoke_syscall, table, nr, tmp, ret, reload=0
#ifdef CONFIG_CPU_SPECTRE
mov \tmp, \nr
cmp \tmp, #NR_syscalls @ check upper syscall limit
movcs \tmp, #0
csdb
badr lr, \ret @ return address
.if \reload
add r1, sp, #S_R0 + S_OFF @ pointer to regs
ldmccia r1, {r0 - r6} @ reload r0-r6
stmccia sp, {r4, r5} @ update stack arguments
.endif
ldrcc pc, [\table, \tmp, lsl #2] @ call sys_* routine
#else
cmp \nr, #NR_syscalls @ check upper syscall limit
badr lr, \ret @ return address
.if \reload
add r1, sp, #S_R0 + S_OFF @ pointer to regs
ldmccia r1, {r0 - r6} @ reload r0-r6
stmccia sp, {r4, r5} @ update stack arguments
.endif
ldrcc pc, [\table, \nr, lsl #2] @ call sys_* routine
#endif
.endm

回看

invoke_syscall tbl, scno, r10, __ret_fast_syscall

这段代码。tbl 是指向的何处呢?

接下来,就简单的介绍一下 syscall table 这个表是怎样形成的。

查看代码我们发现,tbl 表示 sys_call_table 的地址:

adr tbl, sys_call_table @ load syscall table pointer

entry-common.S 中有这样一段代码:

	syscall_table_start sys_call_table

#define COMPAT(nr, native, compat) syscall nr, native
#ifdef CONFIG_AEABI
#include <calls-eabi.S>
#else
#include <calls-oabi.S>
#endif
#undef COMPAT syscall_table_end sys_call_table

calls-eabi.S 文件内容如下:

NATIVE(0, sys_restart_syscall)
NATIVE(1, sys_exit)
NATIVE(2, sys_fork)
NATIVE(3, sys_read)
NATIVE(4, sys_write)
NATIVE(5, sys_open)
NATIVE(6, sys_close)
NATIVE(8, sys_creat)
NATIVE(9, sys_link)
NATIVE(10, sys_unlink)
NATIVE(11, sys_execve)
NATIVE(12, sys_chdir)
NATIVE(14, sys_mknod)
NATIVE(15, sys_chmod)
NATIVE(16, sys_lchown16)
NATIVE(19, sys_lseek)
NATIVE(20, sys_getpid)
...

以上代码中宏的定义如下:

    /* 定义 sys_call_table,并将 __sys_nr 清 0 */
.macro syscall_table_start, sym
.equ __sys_nr, 0
.type \sym, #object
ENTRY(\sym)
.endm /* 检查序号错误,并利用 sys_ni_syscall 填充缺少的序号 */
.macro syscall, nr, func
.ifgt __sys_nr - \nr
.error "Duplicated/unorded system call entry"
.endif
.rept \nr - __sys_nr
.long sys_ni_syscall
.endr
.long \func
.equ __sys_nr, \nr + 1
.endm /* 检查序号是否超过了 __NR_syscalls,如果不足的话,用 sys_ni_syscall 来填充 */
.macro syscall_table_end, sym
.ifgt __sys_nr - __NR_syscalls
.error "System call table too big"
.endif
.rept __NR_syscalls - __sys_nr
.long sys_ni_syscall
.endr
.size \sym, . - \sym
.endm /* NATIVE 宏定义 */
#define NATIVE(nr, func) syscall nr, func

最后会通过SWI中断号调用到相关系统函数,如 sys_open,而 sys_open 的声明形式则如下所示, 1、2、3 由函数的形参所定,如在 source insight 中搜索到 sys_open 的函数定义,可搜索关键词 “SYSCALL_DEFINE3(open”。

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

到这里应该分析完了系统调用的大概过程,感谢大家花费宝贵的时间浏览,如果有什么问题欢迎探讨,后期会进行修改和补充!

部分参考于:www.cnblogs.com/fasionchan/p/9431784.html

Linux 系统调用过程详细分析的更多相关文章

  1. Linux系统调用过程

    1 系统调用的作用 系统调用是操作系统提供给用户(应用程序)的一组接口,每个系统调用都有一个对应的系统调用函数来完成相应的工作.用户通过这个接口向操作系统申请服务,如访问硬件,管理进程等等. 应用程序 ...

  2. Arm Linux系统调用流程详细解析

    Linux系统通过向内核发出系统调用(system call)实现了用户态进程和硬件设备之间的大部分接口. 系统调用是操作系统提供的服务,用户程序通过各种系统调用,来引用内核提供的各种服务,系统调用的 ...

  3. linux系统调用mount全过程分析【转】

    本文转载自:https://blog.csdn.net/skyflying2012/article/details/9748133 系统调用本身是软中断,使用系统调用,内核也陷入内核态,异常处理,找到 ...

  4. arm Linux 系统调用过程

    系统调用是操作系统提供给用户(应用程序)的一组接口,每个系统调用都有一个对应的系统调用函数来完成相应的工作.用户通过这个接口向操作系统申请服务,如访问硬件,管理进程等等.但是因为用户程序运行在用户空间 ...

  5. ORACLE实例恢复过程详细分析--使用dump、BBED等多种工具结合分析

    ---友情提示,内容较多,可以从博文左上的+目录选择小节方便阅读.  实验思路:  --实验相关TRACE文件:http://download.csdn.net/detail/q947817003/6 ...

  6. linux系统调用实现代码分析【转】

    转自:http://linux.chinaunix.net/doc/kernel/2001-07-30/637.shtml 启动早就读完,现在为了写笔记再从启动之后粗略的大体读一遍,基本就是几个大模块 ...

  7. Linux Socket过程详细解释(包括三次握手建立连接,四次握手断开连接)

    我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web 服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠s ...

  8. JVM类加载过程详细分析

    双亲委派加载模型 为什么需要双亲委派加载模型 主要是为了安全,避免用户恶意加载破坏JVM正常运行的字节码文件,比如说加载一个自己写的java.util.HashMap.class.这样就有可能造成包冲 ...

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

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

随机推荐

  1. 洛谷 P1041 错解

    P1041 传染病控制 题目背景 近来,一种新的传染病肆虐全球.蓬莱国也发现了零星感染者,为防止该病在蓬莱国大范围流行,该国政府决定不惜一切代价控制传染病的蔓延.不幸的是,由于人们尚未完全认识这种传染 ...

  2. SSH X11 MAC

    1. X11 for Mac     2. Ubuntu下通过SSH转发X窗口需要具备的条件 原文:http://unix.stackexchange.com/questions/12755/how- ...

  3. echarts--迁徙图特性简介

    $(function() {    loadMapData(); //页面加载时调用封装加载echarts地图的函数});function loadMapData (cityName) {    if ...

  4. linux netlink通信机制

    一.什么是Netlink通信机制  Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口. Netlink 是一种特殊的 s ...

  5. springMVC引入Validation详解

    本文简单介绍如何引入validation的步骤,如何通过自定义validation减少代码量,提高生产力.特别提及:非基本类型属性的valid,GET方法的处理,validation错误信息的统一re ...

  6. Scrapy 1.4 文档 02 安装指南

    安装 Scrapy Scrapy 运行在 Python 2.7 和 Python 3.3 或更高版本上. 如果您使用的是 Anaconda 或 Miniconda,则可以从 conda-forge 通 ...

  7. http cookie管理中cookie police下部分参数含意

      1.compatibility:推荐选择此种策略.这种兼容性设计要求是适应尽可能多的不同的服务器,尽管不是完全按照标准来实现的.如果你遇到了解析 Cookies 的问题,你就可能要用到这一个规范. ...

  8. width和max-width的用处

    width默认是auto啊,你设置max-width相当于没设置width,它按默认值auto自然就是图片宽度咯.max-width很多的场景都是和width配合用的:比如设置一个标签,width是( ...

  9. 你不知道的JavaScript--Item10 闭包(closure)

    JavaScript 闭包究竟是什么? 用JavaScript一年多了,闭包总是让人二丈和尚摸不着头脑.陆陆续续接触了一些闭包的知识,也犯过几次因为不理解闭包导致的错误,一年多了资料也看了一些,但还是 ...

  10. 如何查看selenium的版本号

    方法一: 打开cmd,输入python >>> import selenium >>> help(selenium) Help on package seleniu ...