原文地址:http://blog.csdn.net/yuanlulu/article/details/7481708 下面的分析以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);
......... |
可以看到系统调用最多允许六个参数。
有了上面的知识,基本可以自己增加一个系统调用了,但是系统调用是怎么被调用的呢?
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最后面增加自己的函数
用户空间
#include <unistd.h>
#include
<asm/unistd.h>
#define __NR_mycall xxxx //在用户空间定义自己的调用号
syscall(__NR_mycall, ....其它参数..)
|