ARM Linux系统调用

转载 2015年11月20日 12:50:45
首先要清楚系统调用不会导致进程切换。
下面的分析以2.6.34为例。

内核部分

ARM-Linux的系统调用列表定义在arch/arm/kernel/call.S中:

 *  This file is included thrice in entry-common.S //entry-common.S将会包含这个文件。根据偏移量,获取函数的指针
*/
/* 0 */          CALL(
sys_restart_syscall)
          CALL(sys_exit)
          CALL(sys_fork_wrapper)
          CALL(sys_read)
...................................
          CALL(sys_pipe2)
/* 360 */     CALL(sys_inotify_init1)
          CALL(sys_preadv)
          CALL(sys_pwritev)
          CALL(sys_rt_tgsigqueueinfo)
          CALL(sys_perf_event_open)
/* 365 */     CALL(sys_recvmmsg)
#ifndef syscalls_counted
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
#define syscalls_counted
#endif
.rept syscalls_padding
          CALL(
sys_ni_syscall)
.endr
可以看到2.6.34共支持367个系统调用,最后一个是一个“未实现”的系统调用。除了返回-ENOSYS不做其它工作。

所有系统调用的编号定义在arch/arm/include/asm/unistd.h中:
/*可以看到,使用不同的指令集和二进制接口,系统调号用的基数还不一样*/
#define __NR_OABI_SYSCALL_BASE     0x900000

#if defined(__thumb__) || defined(__ARM_EABI__)
#define __NR_SYSCALL_BASE     0
#else
#define __NR_SYSCALL_BASE     __NR_OABI_SYSCALL_BASE
#endif

/*
* This file contains the system call numbers.
*/

#define __NR_restart_syscall          (__NR_SYSCALL_BASE+  0)
#define __NR_exit               (__NR_SYSCALL_BASE+  1)
#define __NR_fork               (__NR_SYSCALL_BASE+  2)
#define __NR_read               (__NR_SYSCALL_BASE+  3)
#define __NR_write               (__NR_SYSCALL_BASE+  4)
#define __NR_open               (__NR_SYSCALL_BASE+  5)
.................
#define __NR_pipe2               (__NR_SYSCALL_BASE+359)
#define __NR_inotify_init1          (__NR_SYSCALL_BASE+360)
#define __NR_preadv               (__NR_SYSCALL_BASE+361)
#define __NR_pwritev               (__NR_SYSCALL_BASE+362)
#define __NR_rt_tgsigqueueinfo          (__NR_SYSCALL_BASE+363)
#define __NR_perf_event_open          (__NR_SYSCALL_BASE+364)
#define __NR_recvmmsg               (__NR_SYSCALL_BASE+365)

在上面的函数中增加的自己的系统调用之后,可以定义自己的系统调用函数了。比如在fs/open.c中是这么定义open这个系统调用的
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
     long ret;

     if (force_o_largefile())
          flags |= O_LARGEFILE;

     ret = do_sys_open(AT_FDCWD, filename, flags, mode);
     /* avoid REGPARM breakage on x86: */
     asmlinkage_protect(3, ret, filename, flags, mode);
     return ret;
}
定义一个系统调用要用到SYSCALL_DEFINEX(X代表参数个数)这个宏,这个宏的第一个参数是名字,后面的依次是参数类型和名字。
这个宏定义在include/linux/syscall.h中:
#define SYSCALL_DEFINE0(name)        asmlinkage long sys_##name(void)
#endif

#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__)

........
/*所有的系统调用要在这里声明,比如open函数*/
asmlinkage long sys_open(const char __user *filename,
                    int flags, int mode);
.........
可以看到系统调用最多允许六个参数。

有了上面的知识,基本可以自己增加一个系统调用了,但是系统调用是怎么被调用的呢?
下面的内容参考了这篇文章:http://blog.csdn.net/hongjiujing/article/details/6831192
arch/arm/kernel/entry-armv.S中的SWI异常向量有这么一句:
W(ldr)     pc, .LCvswi + stubs_offset
arch/arm/kernel/entry-armv.S中LCvswi被定义为:
.LCvswi:
     .word     vector_swi
vector_swi例程定义在arch/arm/kernel/entry-common.S中。这个例程会保护现场,获取调用号,然后使用调用号作为索引查找系统调用表并调用相应的函数,最后通过例程ret_fast_syscall来返回。
(不太懂ARM汇编就少说点)

用户空间部分

用户空间需要调用一些硬件体系相关的特殊指令陷入内核,触发内从异常中断向量表中调用系统调用例程。但是用户空间该怎么实现呢?
《Linux内核设计与实现》说的添加系统调用的方法过时了,起码在ubuntu11.04上不是那样的,那些_syscalln()函数怎么都找不到,只在/usr/include/unistd.h中找到下面一个接口:
extern long int syscall (long int __sysno, ...) __THROW;
真正的系统调用编号定义在/usr/include/asm/unistd_32.h。
写段程序验证一下,
#include <sys/stat.h>
#include <asm/unistd.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
        syscall(__NR_chmod, "/opt/test.c", S_IXUSR);

        return 0;
}

这段代码模拟了系统调用chmod,作用就是将/opt/test.c的权限改为只对所有者可执行,其它权限都去掉。
使用“man 2 chmod”可以查看chmod的man手册。

将上面的代码交叉编译之后复制到ARM开发板上,执行结果如下:
[root@EasyARM3250 opt]# ls
test*   test.c
[root@EasyARM3250 opt]# ls -l test.c
-rw-------    1 root     root           164 Jan  1 01:11 test.c
[root@EasyARM3250 opt]# ./test
[root@EasyARM3250 opt]# ls -l test.c
---x------    1 root     root           164 Jan  1 01:11 test.c*
[root@EasyARM3250 opt]#
可见执行很成功。

另外,所有的系统调用都是经过C库间接调用的,《unix环境高级编程第三版》1.11小节“系统调用和库函数”中有句话很经典:Unix所使用的技术是为每个系统调用在标准C库中设置一个具有相同名字的函数。这一点在http://blog.csdn.net/hongjiujing/article/details/6831192中有所体现。

总结---如何添加arm linux的系统调用(2.6.34)

内核:
     1.在内核源码中实现系统调用函数
         可以参考fs/open.c中的open函数
          SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode) ......

     2.定义函数调用号
     在arch/arm/include/asm/unistd.h中增加自己的函数调用编号

     3.声明新增的系统调用函数
     在include/linux/syscall.h中声明自己干刚定义的函数,如:
     asmlinkage long sys_open(const char __user *filename,
                    int flags, int mode);

     4.加入调用函数指针列表
     在arch/arm/kernel/call.S最后面增加自己的函数
注意:arm64中内核的新增系统调用略有些差异:
1.在内核源码中实现系统调用函数
         可以参考fs/open.c中的open函数
          SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode) ......
    2.声明新增的系统调用函数
     在include/linux/syscall.h中声明自己干刚定义的函数,如:
     asmlinkage long sys_open(const char __user *filename,
                    int flags, int mode);
3.加入调用函数指针列表
在include/uapi/asm-generic/unistd.h中创建系统调用条目增加__NR_syscalls 数值
#define __NR_open 1024
__SYSCALL(__NR_open, sys_open)


用户空间
#include <unistd.h>
#include <asm/unistd.h>
#define  __NR_mycall  xxxx  //在用户空间定义自己的调用号
syscall(__NR_mycall, ....其它参数..)     

============================================
作者:yuanlulu
http://blog.csdn.net/yuanlulu
版权没有,但是转载请保留此段声明

============================================

相关文章推荐

arm系统调用

一 系统调用用户接口 1 int open(const char *filename, int oflag,mode_t mode); 打开file,方式为oflag("O_RDONLY"即0,"O_...

ARM-Linux系统调用流程

旧式x86平台上的系统调用由int 0x80中断实现,后来对于新式CPU,Linux使用了sysenter方式。     在ARM平台上,使用了swi中断来实现系统调用的跳转。     swi指令...

Arm Linux系统调用流程详细解析-SWI

转自:http://www.diybl.com/course/6_system/linux/Linuxjs/20090515/167024.html Unix系统通过向内核发出系统调用(sy...
  • MyArrow
  • MyArrow
  • 2011年12月02日 20:45
  • 10171

ARM Linux系统调用的原理

ARM Linux系统调用的原理 操作系统为在用户态运行的进程与硬件设备进行交互提供了一组接口。在应用程序和硬件之间设置一个额外层具有很多优点。首先,这使得编程更加容易,把用户从学习硬件设备的低级编...

从功能模块角度看ARM Cortex-M各处理器区别

ARM Cortex-M处理器家族发展至今(2016),已有5代产品,分别是CM0/CM0+、CM1、CM3、CM4、CM7,为了能做到Cortex-M软件重用,ARM公司在设计Cortex-M处理器...

linux 添加系统调用的方法(arm架构)

在linux中,系统调用是非常重要的一个机制。本文首先探讨了系统调用需要哪些文件的支持,之后讨论如何增加自己的系统调用。...

arm linux系统调用分析

  • 2012年11月10日 21:45
  • 135KB
  • 下载

ARM Linux系统调用详细分析

文件: linux-2.6.30.4/arch/arm/kernel/entry-common.S linux-2.6.30.4/arch/arm/kernel/calls.S 在entry-com...
  • liduxun
  • liduxun
  • 2015年08月31日 00:39
  • 2544

Arm Linux系统调用流程详细解析-SWI

转自:http://www.diybl.com/course/6_system/linux/Linuxjs/20090515/167024.html   Unix系统通过向内核发出系统调用...

linux系统调用过程解析(基于ARM处理器)

基于ARM处理器的linux系统调用从用户态到内核再返回用户态解析。
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ARM Linux系统调用
举报原因:
原因补充:

(最多只允许输入30个字)