首页 > 系统相关 >Linux系统调用实现简析

Linux系统调用实现简析

时间:2022-12-09 23:31:07浏览次数:45  
标签:__ swi 调用 int vectors 简析 vector Linux

1. 前言

限于作者能力水平,本文可能存在的谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 背景

本篇基于 Linux 4.14 + ARM 32 + glibc-2.31 进行分析。

3. 系统调用的实现

3.1 系统调用的发起

3.1.1 起于用户空间

我们随意挑选一个系统调用,如常见的 write()glibc-2.31 对其实现如下:

ssize_t
__libc_write (int fd, const void *buf, size_t nbytes)
{
  	return SYSCALL_CANCEL (write, fd, buf, nbytes);
}

// 中间省略多个宏定义,对这些细节感兴趣的读者可以自行阅读 glibc 代码。
// ...

# define INTERNAL_SYSCALL_RAW(name, err, nr, args...)		\
  ({								\
       register int _a1 asm ("r0"), _nr asm ("r7");		\
       LOAD_ARGS_##nr (args)					\
       _nr = name;						\
       asm volatile ("swi	0x0	@ syscall " #name	\
		     : "=r" (_a1)				\
		     : "r" (_nr) ASM_ARGS_##nr			\
		     : "memory");				\
       _a1; })

总之,系统调用语句 return SYSCALL_CANCEL (write, fd, buf, nbytes) 最终展开如下(为方便阅读,对最终结果的格式稍作了调整):

return ({
	long int sc_ret;
	
	int sc_cancel_oldtype = LIBC_CANCEL_ASYNC ();
	sc_ret = ({
		unsigned int _sys_result = ({
				register int _a1 asm ("r0")/* 参数fd从寄存器r0传入 */, _nr asm ("r7")/* 系统调用编号从寄存器 r7 传入 */;
				int _a3tmp = (int) (nbytes);
				int _a2tmp = (int) (buf);
				int _a1tmp = (int) (fd);
				_a1 = _a1tmp;
				register int _a2 asm ("a2") = _a2tmp; /* 参数 buf 从寄存器 r1 (即a2) 传入 */
				register int _a3 asm ("a3") = _a3tmp; /* 参数 @nbytes 从寄存器 r2 (即 a3) 传入 */
					
				_nr = __NR_write; /* 赋值系统调用编号 */
				/* arm32 通过 swi 指令,发起系统调用 */
				asm volatile ("swi	0x0	@ syscall __NR_write"
							: "=r" (_a1)
							: "r" (_nr), "r" (_a1), "r" (_a2), "r" (_a3)
							: "memory");
					_a1; /* 系统调用返回值,从寄存器 r0 传回 */
			});
		if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_sys_result, ), 0))
		{
			__set_errno (INTERNAL_SYSCALL_ERRNO (_sys_result, )); /* 调用出错,设置错误码 errno */
			_sys_result = (unsigned int) -1; /* 调用出错,返回 -1 */
		}
		(int) _sys_result; /* 系统调用返回值: sc_ret = _sys_result; */
	});
	LIBC_CANCEL_RESET (sc_cancel_oldtype);
	sc_ret; /* write() 调用的返回值 */
});

注: 寄存器 r0/a1, r1/a2, r2/a3 是等同的,关于ARM32平台调用规范寄存器的更多细节,可参看ARM32调用规范文档《IHI0042J_2020Q2_aapcs32.pdf》表格 Table 6.1: Table 2, Core registers and AAPCS usage 。 从上面代码的分析中,我们了解到ARM32平台的系统调用,是通过 swi 指令发起,以及系统调用参数传递的细节。接下来,我们继续分析系统调用进入内核空间后的工作细节。

3.1.2 进入内核空间

系统调用的过程,是和具体硬件架构相关的,在继续讨论内核空间系统调用的工作细节之前,我们先来聊一点 ARM32 架构和系统调用相关的内容。 先了解下 ARM32 CPU 的几种工作模式: image.png 以及各 CPU 工作模式下的寄存器分布状况: image.png 从上图表格可以看出:

. 有些寄存器是所有模式共享的,如寄存器 R0~R7 ;
. 有些寄存器模式独立的,如 R13 和 R14 ,User 和 System 模式共用一组空间,而 Supervisor 等其它模式使用各自独立的空间(R13_xxx,R14_xxx)。

再看一下 ARM32 异常和CPU模式的对应关系: image.png 自从用户空间 swi 指令的运行,会导致 ARM32 CPU 产生一个异常,根据上图表格,CPU 将进入 Supervisor 模式,ARM32 CPU 进入异常时,还会自动做如下动作:

R14_svc = swi 下一条指令的地址(即用户空间的返回地址)
SPSR_svc = CPSR (即CPU当前状态寄存器,模式为 User)
CPSR[4:0] = 0b10011(切换到 Supervisor 模式)
CPSR[7] = 1(禁用当前 CPU 的一般中断)
...
PC = 异常向量地址(即跳转到异常向量地址处执行)

接下来,我们要找到内核 SWI 异常向量的定义位置,因为程序流程将跳转到该处执行。我们先要找到中断向量表,从 ARM32 架构的链接脚本片段:

/* @arch/arm/kernel/vmlinux.lds.S */

__vectors_start = .;
.vectors 0xffff0000 : AT(__vectors_start) {
	*(.vectors)
}
. = __vectors_start + SIZEOF(.vectors);
__vectors_end = .;

/* 位于中断向量表偏移 0x1000 处 */
.stubs ADDR(.vectors) + 0x1000 : AT(__stubs_start) {
	*(.stubs)
}
. = __stubs_start + SIZEOF(.stubs);
__stubs_end = .;

我们找到 swi 指令异常中断向量入口 vector_swi

/* @arch/arm/kernel/armv.S */

	.section .stubs, "ax", %progbits
	.word	vector_swi /* swi 指令异常中断向量入口 */

	...

	.globl	vector_fiq

	.section .vectors, "ax", %progbits
.L__vectors_start:
	W(b)	vector_rst
	W(b)	vector_und
	W(ldr)	pc, .L__vectors_start + 0x1000 /* 软中断向量(swi) */
	W(b)	vector_pabt
	W(b)	vector_dabt
	W(b)	vector_addrexcptn
	W(b)	vector_irq
	W(b)	vector_fiq

兜兜转转,我们终于进入了我们的正题,内核空间系统调用流程的分析:

/*=============================================================================
 * SWI handler
 *-----------------------------------------------------------------------------
 */

	.align	5
ENTRY(vector_swi)
...

3.2 系统调用的自动重新发起

3.3 系统调用的返回

4. 添加系统调用

5. 参考资料

《ARM Architecture Reference Manual.pdf》
《IHI0042J_2020Q2_aapcs32.pdf》

标签:__,swi,调用,int,vectors,简析,vector,Linux
From: https://blog.51cto.com/u_4594296/5926721

相关文章

  • linux shell
    frrinit.shif[-r"/lib/lsb/init-functions"];then./lib/lsb/init-functionselselog_success_msg(){echo"$@"}log_warning_msg()......
  • linux 安装 nginx,并且注册成一个服务
    * 一键安装四个依赖yum-yinstallgcczlibzlib-develpcre-developensslopenssl-devel* 安装nginx【如果报:-bash:wget:commandnotfound,先安装wget:yum-yinsta......
  • vscode中输入``自动将光标后面一个单词选中,左右加入<w>和</w>标签 - snippets 的命令
    需求vscode中输入``自动将光标后面一个单词选中,左右加入和标签步骤0准备需要安装插件vim-这里的点击两次按键激活的快捷键,这个插件可以设置macros-一次执行多个......
  • 聊聊我学linux的历程
    今天给大家分享一下我学习linux的历程,我喜欢上linux系统完全就是机缘巧合,大学时上的是网络工程专业,说来惭愧一直到大二上学期我还不知linux为何物,唯一接触过的就是电影里面......
  • linux mailx&dingding机器人报警
    前言:采用devops的思想来确认做本文内容目的作为<用户角色>我想要<结果>以便于<目的>作为运维人员,我想要服务器故障时候能够进行报警,以便于即使处理服务器故障、保障服......
  • linux代码相关的工具
    函数调用关系codevizcalltreegprof,kprofhttp://blog.chinaunix.net/uid-20749137-id-718762.htmlgraphvizgdb&backtracestracediagramdesigner可以画代码的关系图 应......
  • 36、RK3399Pro 环境搭建和Yolov5 c++调用opencv进行RKNN模型部署和使用
    基本思想:记录rk3399pro配置环境和c++npu开发记录,主要想搞一份c++代码和其它图像算法结合一下,好进行部署,淘宝链接见附录 需要的python3.7对应的aarch64的whl包:包含opencv......
  • Linux 资料大全
    Helloall,给大家分享一些​​Linux学习资料​​,包含:社区网站、在线教程、命令大全、在线模拟器、经典书籍、镜像站点等...从入门到进阶,应有尽有。无论你是小白,还是Linux......
  • LINUX下EPOLL等不错的文章收藏
    1通俗讲解异步,非阻塞和IO复用​​​https://www.zybuluo.com/phper/note/595507​​​2知乎问答https://www.zhihu.com/question/20122137/......
  • linux 下的sar工具命令小结
    1安装  tarzxvf xxx.tar.gz  ./configure  make  makeinstall 2 使用pidstat 2 5 //每隔2秒,显示5次,所有活动进程的CPU使用情况pidstat -p......