linux 系统调用流程分析

news2024/9/30 3:18:38
  • x86

1.系统调用

  系统调用是用户空间程序与内核交互的主要机制。系统调用与普通函数调用不同,因为它调用的是内核里的代码。使用系统调用时,需要特殊指令以使处理器权限转换到内核态。另外,被调用的内核代码由系统调用号来标识,而不是函数地址。系统调用流程如下图所示:
在这里插入图片描述

2.x86 系统调用实现原理

  比如创建子进程,内核提供fork系统调用作为接口。如果用户态程序想调用这个内核提供的接口,其对应的汇编语句

movq $57, %rax
syscall

  syscall指令会先查看此时RAX的值,然后找到系统调用号为那个值的系统调用,然后执行相应的系统调用。在系统调用列表中找到,fork这个系统调用的系统调用号是57。于是,把57放入rax寄存器中,然后使用了syscall指令。这就是让内核执行了fork。

2.1.调用约定

  系统调用往往会有许多参数,比如说open系统操作,在include/linux/syscalls.h中找到其对应的C语言接口为

asmlinkage long sys_open(const char __user *filename, int flags, umode_t mode);

  它接受三个参数。那么,参数传递是按照什么规定呢?事实上,当涉及到系统调用时,调用约定与用户态程序一般的调用约定并不相同。x86-64 ABI文档 第A.2.1节,描述了调用约定:

The Linux AMD64 kernel uses internally the same calling conventions as user-level applications (see section 3.2.3 for details). User-level applications that like to call system calls should use the functions from the C library. The interface between the C library and the Linux kernel is the same as for the user-level applications with the following differences:

User-level applications use as integer registers for passing the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9. The kernel interface uses %rdi, %rsi, %rdx, %r10, %r8 and %r9.

A system-call is done via the syscall instruction. The kernel clobbers registers %rcx and %r11 but preserves all other registers except %rax.

The number of the syscall has to be passed in register %rax.

System-calls are limited to six arguments, no argument is passed directly on the stack.

Returning from the syscall, register %rax contains the result of the system-call. A value in the range between -4095 and -1 indicates an error, it is -errno.

Only values of class INTEGER or class MEMORY are passed to the kernel.

  可以看出,系统调用约定了以下几个方面:

  • 参数相关
  • 系统调用号
  • 系统调用指令
  • 返回值及错误码

  这个规范就称为内核接口的调用约定,可以从第一点就显著地看到,这个调用约定与用户态的程序是不同的。也就是说,如果我们用编译器直接编译

long sys_open(const char *pathname, int flags, mode_t mode);

  那么,编译出来的可执行程序会认为,这个函数是用户态函数,其传参仍然是按 %rdi, %rsi, %rdx, %rcx, %r8, %r9的顺序,与内核接口不符。因此,gcc提供了一个标签asmlinkage来标记这个函数是内核接口的调用约定:

asmlinkage long sys_open(const char *pathname, int flags, mode_t mode);

  当函数前面有这个标签时,编译器编译出的可执行程序就会认为是按内核接口的调用约定对这个函数进行调用的。

2.1 系统调用的入参

2.1.1 参数顺序
在这里插入图片描述
对应关系查看arch/x86/entry/entry_64.S:

 435 /*
 436  * System call entry. Upto 6 arguments in registers are supported.
 437  *
 438  * SYSCALL does not save anything on the stack and does not change the
 439  * stack pointer.
 440  */
 441
 442 /*
 443  * Register setup:
 444  * rax  system call number
 445  * rdi  arg0
 446  * rcx  return address for syscall/sysret, C arg3
 447  * rsi  arg1
 448  * rdx  arg2
 449  * r10  arg3  (--> moved to rcx for C)
 450  * r8   arg4
 451  * r9   arg5
 452  * r11  eflags for syscall/sysret, temporary for C
 453  * r12-r15,rbp,rbx saved by C code, not touched.
 454  *
 455  * Interrupts are off on entry.
 456  * Only called from user space.
 457  *
 458  * XXX  if we had a free scratch register we could save the RSP into the stack frame
 459  *      and report it properly in ps. Unfortunately we haven't.
 460  *
 461  * When user can change the frames always force IRET. That is because
 462  * it deals with uncanonical addresses better. SYSRET has trouble
 463  * with them due to bugs in both AMD and Intel CPUs.
 464  */

2.1.2 参数数量

  系统调用参数限制为6个。

2.1.3 参数类型

  参数类型限制为 INTEGER 和 MEMORY。x86-64 ABI 定义第3.2.3节 Parameter Passing描述:

INTEGER This class consists of integral types that fifit into one of the general purpose registers.
MEMORY This class consists of types that will be passed and returned in memory via the stack.

2.2 返回值及错误码
  当从系统调用返回时,%rax里保存着系统调用结果;如果是-4095 至 -1之间的值,表示调用过程中发生了错误。

2.3 系统调用号

  系统调用号通过%rax传递。

2.4 系统调用指令

  系统调用通过指令syscall来执行。

3.将中断号与系统调用函数绑定

  系统调用函数 (system_call) 是系统调用的总入口,将其与中断号 0x80绑定。

arch/x86/kernel/traps.c
#define SYSCALL_VECTOR 0x80
set_system_trap_gate(SYSCALL_VECTOR, &system_call);

3.1.系统调用函数实现

  比如创建子进程,内核提供fork系统调用作为接口。如果用户态程序想调用这个内核提供的接口,其对应的汇编语句

movq $57, %rax
syscall

  syscall指令会先查看此时RAX的值,然后找到系统调用号为那个值的系统调用,然后执行相应的系统调用。在系统调用列表中找到,fork这个系统调用的系统调用号是57。于是,把57放入rax寄存器中,然后使用了syscall指令。这就是让内核执行了fork。

  由于需要从用户态栈切换到内核态栈,需要栈切换,因此CPU会将用户态的栈相关的参数(oldss, oldesp)压栈,再调用system_call。

  在 system_call 内部:

  将所有寄存器压入内核栈,其中 ebx, ecx, edx 存放程序的参数以 eax 为偏移量,在 sys_call_table 中找到指定的系统调用的地址,其中sys_call_table 定义了所有的系统函数的地址。

从以上可以看出参数传递的方式:

  • 从用户态到内核态通过寄存器传递
  • 内核函数通过栈读取,即通过栈传递
arch/x86/kernel/entry_32.S
.macro SAVE_ALL
	cld
	PUSH_GS
	pushl %fs
	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
	movl $(__KERNEL_PERCPU), %edx
	movl %edx, %fs
	SET_KERNEL_GS %edx
.endm

ENTRY(system_call)
	pushl %eax			# save orig_eax
	SAVE_ALL
	GET_THREAD_INFO(%ebp)		# system call tracing in operation / emulation
	testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
	jnz syscall_trace_entry
	cmpl $(nr_syscalls), %eax
	jae syscall_badsys
syscall_call:
	call *sys_call_table(,%eax,4)
	movl %eax,PT_EAX(%esp)		# store the return value

arch/x86/kernel/syscall_table_32.S:

ENTRY(sys_call_table)
	.long sys_restart_syscall	/* 0 - old "setup()" system call, used for restarting */
	.long sys_exit
	.long ptregs_fork
	.long sys_read
	.long sys_write
	.long sys_open		/* 5 */
	.long sys_close
	...
	.long sys_rt_tgsigqueueinfo	/* 335 */
	.long sys_perf_event_open
	.long sys_recvmmsg
	.long sys_fanotify_init
	.long sys_fanotify_mark
	.long sys_prlimit64		/* 340 */

refer to

  • https://juejin.cn/post/7203681024236355639
  • https://wenfh2020.com/2021/09/05/kernel-syscall/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1235183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从android.graphics.Path中取出Point点,Kotlin

从android.graphics.Path中取出Point点&#xff0c;Kotlin /*** 从一条Path中获取多少个Point点*/private fun getPoints(path: Path, pointCount: Int): Array<FloatPoint?> {val points arrayOfNulls<FloatPoint>(pointCount)val pm PathMeasure(path, false)…

[Linux] 进程入门

&#x1f4bb;文章目录 &#x1f4c4;前言计算机的结构体系与概念冯诺依曼体系结构操作系统概念目的与定位 进程概念描述进程-PCBtask_struct检查进程利用fork创建子进程 进程状态进程状态查看僵尸进程孤儿进程 &#x1f4d3;总结 &#x1f4c4;前言 作为一名程序员&#xff0c…

HarmonyOS云开发基础认证【最新题库 满分答案】

系列文章 HarmonyOS应用开发者基础认证【闯关习题 满分答案】 HarmonyOS应用开发者基础认证【满分答案】 HarmonyOS云开发基础认证【最新题库 满分答案】 目录 系列文章一、判断题二、单选题三、多选题 一、判断题 1.应用架构的演进依次经历了微服务架构、单体架构、Serverle…

Python (十二) 文件

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一份大厂面试资料《史上最全大厂面试题》&#xff0c;Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …

【深度优先搜索遍历算法的实现,广度优先遍历(BFS-Breadth_First Search),构造最小生成树】

文章目录 深度优先搜索遍历算法的实现邻接矩阵表示的无向图深度遍历实现&#xff1a;DFS算法分析 广度优先遍历&#xff08;BFS-Breadth_First Search&#xff09;构造最小生成树 深度优先搜索遍历算法的实现 邻接矩阵表示的无向图深度遍历实现&#xff1a; 实现深度优先遍历的…

MATLAB | 绘图复刻(十三) | 带NaN图例的地图绘制

有粉丝问我地图绘制如何添加NaN&#xff0c;大概像这样&#xff1a; 或者这样&#xff1a; 直接上干货&#xff1a; 原始绘图 假设我们有这样的一张图地图&#xff0c;注意运行本文代码需要去matlab官网下载Mapping Toolbox工具箱&#xff0c;但是其实原理都是相似的&…

俄罗斯网络间谍组织在有针对性的攻击中部署LitterDrifter USB蠕虫

导语 俄罗斯网络间谍组织最近在针对乌克兰实体的攻击中&#xff0c;部署了一种名为LitterDrifter的USB蠕虫。这种蠕虫具有自动传播恶意软件的功能&#xff0c;并与威胁行为者的命令和控制服务器进行通信。该组织被称为Gamaredon&#xff0c;其攻击行动被认为是大规模的&#xf…

level=warning msg=“failed to retrieve runc version: signal: segmentation fault“

安装docker启动后&#xff0c;发现里面没有runc版本信息 目前看是少了runc组件 那我们安装runc https://github.com/opencontainers/runc/releases/download/v1.1.10/runc.amd64 [rootlocalhost ~]# mv runc.amd64 /usr/bin/runc mv&#xff1a;是否覆盖"/usr/bin/runc&q…

基于阶梯碳交易的含P2G-CCS耦合和燃气掺氢的虚拟电厂优化调度matlab程序

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 参考文献&#xff1a; 基于阶梯碳交易的含P2G-CCS耦合和燃气掺氢的虚拟电厂优化调度——陈登勇 主要内容&#xff1a; 以碳交易和碳封存成本、燃煤机组启停和煤耗成本、弃风成本、购气成本之和为目标函数&…

初识树(c语言)

树 定义&#xff1a;树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。 有一个特殊的结点&#xff0c;称为根结点&#xff0c;根节点没有前驱结点 除根节点外&#xff0c;其余结点被分成M(M>0)个互不相交…

什么是PyQt?

什么是Qt? Qt是一个著名的跨平台C图形用户界面应用程序开发框架。它由Qt公司开发,于1995年首次发布。Qt支持各种桌面,嵌入式和移动平台。 Qt的特点包括: 跨平台支持:Qt应用程序可以编译到多种平台运行,包括Windows,Mac,Linux,Android,iOS等。这大大简化了跨平台应用程序的开…

【Linux】文件操作

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析&#xff08;3&#xff09; 目录 &#x1f449;&#x1f3fb;文件是什么&#xff1f;&am…

如何让bug远离你?

想让bug远离你&#xff0c;当然是靠佛祖保佑~ /** *************************************************************************** ******************** ********************* ******************** COPYRIGHT INFORMATION *…

关于使用Java-JWT的笔记

Token的组成规则 一个token分三部分&#xff0c;按顺序为&#xff1a;头部&#xff08;header)&#xff0c;载荷&#xff08;payload)&#xff0c;签证&#xff08;signature) 由三部分生成token &#xff0c;三部分之间用“.”号做分隔。 例如&#xff1a;“eyJhbGciOiJIUzI1…

java: 无效的目标发行版: 17 问题解决

今天在写完类点击运行后显示java: 无效的目标发行版: 17 网上查询了一番&#xff0c;发现有几个地方需要注意。 还有一个就是设置中&#xff0c;下面的就是我本次问题所在&#xff0c;不知道为什么&#xff0c;他自动添加了下面的东西 一个方法是把目标字节码版本改为正确的&a…

Linux:wget后台下载/查看后台任务进度

1. 后台下载 使用wget -b url&#xff1a; wget -b http://cn.wordpress.org/wordpress-3.1-zh_CN.zip后台任务启动后&#xff0c;会返回两段话&#xff0c;第一段返回一个pid&#xff0c;代表这个后台任务的进程&#xff0c;并且我们可以kill掉这个id来终止此次下载&#x…

听GPT 讲Rust源代码--src/librustdoc(2)

题图来自 Chromium项目将支持Rust编程语言[1] File: rust/src/librustdoc/html/render/search_index.rs 在Rust源代码中&#xff0c;rust/src/librustdoc/html/render/search_index.rs文件的作用是生成搜索索引&#xff0c;用于在Rust文档页面上进行关键字搜索。该文件实现了一…

C语言童年生活二三事(ZZULIOJ1091:童年生活二三事(多实例测试))

题目描述 Redraiment小时候走路喜欢蹦蹦跳跳&#xff0c;他最喜欢在楼梯上跳来跳去。 但年幼的他一次只能走上一阶或者一下子蹦上两阶。 现在一共有N阶台阶&#xff0c;请你计算一下Redraiment从第0阶到第N阶共有几种走法。 输入&#xff1a;输入包括多组数据。 每组数据包括一…

VMware——WindowServer2012R2环境安装mysql5.7.14解压版_互为主从(图解版)

目录 一、服务器信息二、192.168.132.35服务器上安装mysql&#xff08;主&#xff09;2.1、环境变量配置2.2、安装2.2.1、修改配置文件内容2.2.2、初始化mysql并指定超级用户密码2.2.3、安装mysql服务2.2.4、启动mysql服务2.2.5、登录用户管理及密码修改2.2.6、开启远程访问 三…

电力工作记录仪、智能安全帽、智能布控球助力智能电网建设

电力行业的建设和发展是国家经济发展的重要支撑&#xff0c;而智能电网作为电力系统的重要组成部分&#xff0c;它的安全高效运行关乎到整个电力系统乃至民生的稳定和安全。为了加快国家经济的发展以及满足人们对电力的需求和用电可靠性的要求&#xff0c;国家早在十二规划中就…