你知道Arm Linux系统调用流程?

2020-08-04来源: elecfans关键字:Arm  Linux系统  调用流程

Linux系统通过向内核发出系统调用(system call)实现了用户态进程和硬件设备之间的大部分接口。

系统调用是操作系统提供的服务,用户程序通过各种系统调用,来引用内核提供的各种服务,系统调用的执行让用户程序陷入内核,该陷入动作由swi软中断完成。


1、用户可以通过两种方式使用系统调用:

第一种方式是通过C库函数,包括系统调用在C库中的封装函数和其他普通函数。

第二种方式是使用_syscall宏。2.6.18版本之前的内核,在include/asm-i386/unistd.h文件中定义有7个_syscall宏,分别是:

_syscall0(type,name) _syscall1(type,name,type1,arg1) _syscall2(type,name,type1,arg1,type2,arg2) _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5,type6,arg6)


其中,type表示所生成系统调用的返回值类型,name表示该系统调用的名称,typeN、argN分别表示第N个参数的类型和名称,它们的数目和_syscall后面的数字一样大。


这些宏的作用是创建名为name的函数,_syscall后面跟的数字指明了该函数的参数的个数。


比如sysinfo系统调用用于获取系统总体统计信息,使用_syscall宏定义为:

_syscall1(int, sysinfo, struct sysinfo *, info);

展开后的形式为:

int sysinfo(struct sysinfo * info) {   long __res;   __asm__ volatile("int $0x80" : "=a" (__res) : "0" (116),"b" ((long)(info)));   do {     if ((unsigned long)(__res) >= (unsigned long)(-(128 + 1)))     {       errno = -(__res);       __res = -1;     }     return (int) (__res);   } while (0); }


可以看出,_syscall1(int, sysinfo, struct sysinfo *, info)展开成一个名为sysinfo的函数,原参数int就是函数的返回类型,原参数struct sysinfo *和info分别构成新函数的参数。


在程序文件里使用_syscall宏定义需要的系统调用,就可以在接下来的代码中通过系统调用名称直接调用该系统调用。下面是一个使用sysinfo系统调用的实例。


代码清单5.1  sysinfo系统调用使用实例

#include #include #include #include /* for struct sysinfo */ _syscall1(int, sysinfo, struct sysinfo *, info); int main(void) {   struct sysinfo s_info;   int error;  error = sysinfo(&s_info);   printf("code error = %d/n", error);   printf("UpTIme = %lds/nLoad:       1 min %lu / 5 min %lu / 15 min %lu/n"       "RAM: total %lu / free %lu / shared %lu/n"       "Memory in buffers = %lu/nSwap: total %lu / free %lu/n"   "Number of processes = %d/n",   s_info.upTIme,       s_info.loads[0], s_info.loads[1], s_info.loads[2],       s_info.totalram, s_info.freeram, s_info.sharedram, s_info.bufferram, s_info.totalswap, s_info.freeswap,       s_info.procs);   exit(EXIT_SUCCESS); }


但是自2.6.19版本开始,_syscall宏被废除,我们需要使用syscall函数,通过指定系统调用号和一组参数来调用系统调用。


syscall函数原型为:

int syscall(int number, ...);

其中number是系统调用号,number后面应顺序接上该系统调用的所有参数。下面是getTId系统调用的调用实例。


代码清单5.2  getTId系统调用使用实例

#include #include #include #define __NR_gettid 224 int main(int argc, char *argv[]) { pid_t tid; tid = syscall(__NR_gettid); }

大部分系统调用都包括了一个SYS_符号常量来指定自己到系统调用号的映射,因此上面第10行可重写为:

tid = syscall(SYS_gettid);


2 系统调用与应用编程接口(API)区别

应用编程接口(API)与系统调用的不同在于,前者只是一个函数定义,说明了如何获得一个给定的服务,而后者是通过软件中断向内核发出的一个明确的请求。POSIX标准针对API,而不针对系统调用。Unix系统给程序员提供了很多API库函数。libc的标准c库所定义的一些API引用了封装例程(wrapper routine)(其唯一目的就是发布系统调用)。通常情况下,每个系统调用对应一个封装例程,而封装例程定义了应用程序使用的API。反之则不然,一个API没必要对应一个特定的系统调用。从编程者的观点看,API和系统调用之间的差别是没有关系的:唯一相关的事情就是函数名、参数类型及返回代码的含义。然而,从内核设计者的观点看,这种差别确实有关系,因为系统调用属于内核,而用户态的库函数不属于内核。


大部分封装例程返回一个整数,其值的含义依赖于相应的系统调用。返回-1通常表示内核不能满足进程的请求。系统调用处理程序的失败可能是由无效参数引起的,也可能是因为缺乏可用资源,或硬件出了问题等等。在libd库中定义的errno变量包含特定的出错码。每个出错码定义为一个常量宏。


当用户态的进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。因为内核实现了很多不同的系统调用,因此进程必须传递一个名为系统调用号(system call number)的参数来识别所需的系统调用。所有的系统调用都返回一个整数值。这些返回值与封装例程返回值的约定是不同的。在内核中,整数或0表示系统调用成功结束,而负数表示一个出错条件。在后一种情况下,这个值就是存放在errno变量中必须返回给应用程序的负出错码。


3 系统调用执行过程

ARM Linux系统利用SWI指令来从用户空间进入内核空间,还是先让我们了解下这个SWI指令吧。SWI指令用于产生软件中断,从而实现从用户模式变换到管理模式,CPSR保存到管理模式的SPSR,执行转移到SWI向量。在其他模式下也可使用SWI指令,处理器同样地切换到管理模式。指令格式如下:

SWI{cond} immed_24

其中:

immed_24  24位立即数,值为从0——16777215之间的整数。

使用SWI指令时,通常使用一下两种方法进行参数传递,SWI异常处理程序可以提供相关的服务,这两种方法均是用户软件协定。SWI异常中断处理程序要通过读取引起软件中断的SWI指令,以取得24为立即数。


1)、指令中24位的立即数指定了用户请求的服务类型,参数通过通用寄存器传递。如:

MOV R0,#34SWI 12

2)、指令中的24位立即数被忽略,用户请求的服务类型有寄存器R0的只决定,参数通过其他的通用寄存器传递。如:

MOV R0, #12MOV R1, #34SWI 0

在SWI异常处理程序中,去除SWI立即数的步骤为:首先确定一起软中断的SWI指令时ARM指令还是Thumb指令,这可通过对SPSR访问得到;然后取得该SWI指令的地址,这可通过访问LR寄存器得到;接着读出指令,分解出立即数(低24位)。


由用户空间进入系统调用

通常情况下,我们写的代码都是通过封装的C lib来调用系统调用的。以0.9.30版uClibc中的open为例,来追踪一下这个封装的函数是如何一步一步的调用系统调用的。在include/fcntl.h中有定义:

# define open open64

open实际上只是open64的一个别名而已。

在libc/sysdeps/linux/common/open64.c中可以看到:

extern __typeof(open64) __libc_open64;extern __typeof(open) __libc_open;

可见open64也只不过是__libc_open64的别名,而__libc_open64函数在同一个文件中定义:

libc_hidden_proto(__libc_open64)int __libc_open64 (const char *file, int oflag, ...){ mode_t mode = 0; if (oflag & O_CREAT) { va_list arg; va_start (arg, oflag); mode = va_arg (arg, mode_t); va_end (arg); } return __libc_open(file, oflag | O_LARGEFILE, mode);}libc_hidden_def(__libc_open64)

最终__libc_open64又调用了__libc_open函数,这个函数在文件libc/sysdeps/linux/common/open.c中定义:

libc_hidden_proto(__libc_open)int __libc_open(const char *file, int oflag, ...){ mode_t mode = 0; if (oflag & O_CREAT) { va_list arg; va_start (arg, oflag); mode = va_arg (arg, mode_t); va_end (arg); } return __syscall_open(file, oflag, mode);}libc_hidden_def(__libc_open)

_syscall_open在同一个文件中定义:

static __inline__ _syscall3(int, __syscall_open, const char *, file, int, flags, __kernel_mode_t, mode)

在文件libc/sysdeps/linux/arm/bits/syscalls.h文件中可以看到:

#undef _syscall3#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) type name(type1 arg1,type2 arg2,type3 arg3) { return (type) (INLINE_SYSCALL(name, 3, arg1, arg2, arg3)); }


这个宏实际上完成定义一个函数的工作,这个宏的第一个参数是函数的返回值类型,第二个参数是函数名,之后的参数就如同它的参数名所表明的那样,分别是函数的参数类型及参数名。__syscall_open实际上为:

int __syscall_open (const char * file,int flags, __kernel_mode_t mode){ return (int) (INLINE_SYSCALL(__syscall_open, 3, file, flags, mode));}

INLINE_SYSCALL为同一个文件中定义的宏:

#undef INLINE_SYSCALL#define INLINE_SYSCALL(name, nr, args...) ({ unsigned int _inline_sys_result = INTERNAL_SYSCALL (name, , nr, args); if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_inline_sys_result, ), 0)) { __set_errno (INTERNAL_SYSCALL_ERRNO (_inline_sys_result, )); _inline_sys_result = (unsigned int) -1; } (int) _inline_sys_result; }) #undef INTERNAL_SYSCALL#if !defined(__thumb__)#if defined(__ARM_EABI__)#define INTERNAL_SYSCALL(name, err, nr, args...)

[1] [2] [3] [4]
关键字:Arm  Linux系统  调用流程 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic505239.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:ARM汇编与C混合编程
下一篇:详细介绍构建ARM Linux的交叉编译工具链

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

微软高通合作:给ARM Windows 10设备提供更完善配套
微软今天宣布与高通公司建立合作伙伴关系,为保障 Windows on ARM 上应用的运行提供更完善的配套支持,微软将把具有 FastTrack 测试平台的 App Assure 进行扩展。微软的 App Assure with FastTrack 计划可免费提供给合格的开发人员或客户。App Assure 是一个旨在帮助客户,开发人员和独立软件供应商解决应用程序兼容性问题的程序。另外,也只有高通为 Windows 10 推出了专门的骁龙 8cx/7cx 处理器。高通高管表示:“移动计算的未来是配备 4G / 5G 连接的功能强大,轻薄的长续航 PC。我们很高兴看到 App Assure 计划将帮助确保在搭载骁龙的 Windows
发表于 2020-09-25
Arm Neoverse路线图升级,服务器市场再迎曙光
日前,Arm基础设施事业部高级副总裁兼总经理Chris Bergey表示:“在基础设施领域,Neoverse技术已在新的服务器与系统级芯片设计中崭露头角,其中软件与工具的支持也相当丰富且完整。开发者不仅能看到Neoverse带来性能与效率的大幅提升,也能在基础设施部署体验上享受到更多的设计自由度与灵活性。“实际上,Arm一直以来都想在服务器和PC等市场占据一席之地,但这是一个格外漫长的旅程,经过多年的努力才得以实现。在经历了许多质疑和错误的尝试之后,到了2020年,再没有人可以忽视Arm在服务器市场的地位了。Arm不止具有竞争力,而且在多项指标上实际上处于领先地位。Chris Bergey日前细数了Arm近两年所获得的成就
发表于 2020-09-24
<font color='red'>Arm</font> Neoverse路线图升级,服务器市场再迎曙光
谈谈Zynq SoC里ARM NEON SIMD架构扩展集的使用
在所有Zynq All Programmable SoC 的内部, 你都会发现一个双核的ARM Cortex -A9 MPCore处理器,而且Zynq SoC中的这两个处理器中都设有ARM NEON SIMD架构扩展集。那么为什么您需要采用ARM NEON SIMD扩展集呢?那是因为你可以因此大幅提升你的软件性能。你可能看不到您把关键任务转入Zynq SoC可编程逻辑所获得的那么大幅的加速,但是在很多应用中您都会看到某种加速现象。诀窍就在于NEON扩展集的使用!这一特殊决窍的秘诀在于 Project Ne10的NE10开源库。设立Ne10项目的目的是提供一套通用、有效的函数,可以大大优化ARM架构,包括NEON SIMD扩展集
发表于 2020-09-24
谈谈Zynq SoC里<font color='red'>ARM</font> NEON SIMD架构扩展集的使用
将Zynq SoC上的两个ARM Cortex
到目前为止我们摸索使用过的Zynq All Programmable SoC PS(处理器系统)部分的所有设备都是只利用了一个ARM Cortex-A9处理器内核(内核0),然而在Zynq SoC 的PS部分包含有两个处理器内核,对于很多应用程序来说我们想要利用两个处理器内核,这样才能取得最大化的性能。使用两个处理器内核处理不同的任务被称作非对称多处理机制(AMP),而且包含不同的组合方式:在内核0与内核1上运行不同的操作系统在内核0上运行操作系统,在内核1上执行程序代码(或者反过来)在两个处理器内核上执行不同的程序代码有两种多核处理的方法:对称机制和非对称机制。在我们介绍这两种方法的区别前,我们要先清楚什么是多核处理:“多核处理
发表于 2020-09-24
将Zynq SoC上的两个<font color='red'>ARM</font> Cortex
ARM、DSP、FPGA的区别是什么?
ARM(Advanced RISC Machines)是微处理器行业的一家知名企业,设计了大量高性能、廉价、耗能低的RISC处理器、相关技术及软件。ARM架构是面向低预算市场设计的第一款RISC微处理器,基本是32位单片机的行业标准,它提供一系列内核、体系扩展、微处理器和系统芯片方案,四个功能模块可供生产厂商根据不同用户的要求来配置生产。由于所有产品均采用一个通用的软件体系,所以相同的软件可在所有产品中运行。目前ARM在手持设备市场占有90以上的份额,可以有效地缩短应用程序开发与测试的时间,也降低了研发费用。DSPDSP(digital singnal processor)是一种独特的微处理器,有自己的完整指令系统,是以数字信号
发表于 2020-09-24
Opencv移植和Zedboard测试
继上次生成了ARM架构的链接库之后,我们要把他们拷贝到装载有文件系统的SD卡中即可,在拷贝时,最好是/usr/lib下实践一:将那些lib拷贝到U盘里面,因为之前跑过demo,里面就是一个简易的linux系统,就暂且用他试试了,正常启动后,挂载U盘,这些在之前都有做过,mount /dev/sda1 /mnt拷贝文件夹 cp -R /mnt/lib /usr/ 结果出现提示空间不足,仔细发现问题,发现这个ramdisk镜像最大只能有8M,而整个lib有10.3M,肯定会提示空间不足了,在看看/usr/lib下有哪些文件发现拷贝了一些lib文件进来。这时就想到了实践二的方法、。疑问:然后还有一个就是书上的一句话,如果你使用
发表于 2020-09-24
Opencv移植和Zedboard测试
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved