关闭

ARM Linux系统调用

608人阅读 评论(0) 收藏 举报
分类:
首先要清楚系统调用不会导致进程切换。
下面的分析以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
版权没有,但是转载请保留此段声明

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

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:53087次
    • 积分:964
    • 等级:
    • 排名:千里之外
    • 原创:21篇
    • 转载:120篇
    • 译文:0篇
    • 评论:3条
    最新评论