在linux中,系统调用是非常重要的一个机制。那么,如何才能在自己的代码环境中增加一项系统调用呢?
首先,以“open”系统调用为例,看看系统调用都需要哪些支持:
1 open系统调用的定义在文件<kernel/fs/open.c>中:
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
SYSCALL_DEFINE3 表示该系统调用有三组参数,其定义如下:
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
展开可得到:
SYSCALL_DEFINEx(3, _open, const char __user *, filename, int, flags, umode_t, mode)
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
继续展开,可以得到:
asmlinkage long sys_open(__MAP(3,__SC_DECL,const char __user *, filename, int, flags, umode_t, mode));
static inline long SYSC_open(__MAP(3,__SC_DECL,const char __user *, filename, int, flags, umode_t, mode));
asmlinkage long SyS_open(__MAP(3,__SC_LONG,const char __user *, filename, int, flags, umode_t, mode))
{
long ret = SYSC_open(__MAP(3,__SC_CAST,const char __user *, filename, int, flags, umode_t, mode));
__MAP(3,__SC_TEST,const char __user *, filename, int, flags, umode_t, mode);
__PROTECT(3, ret,__MAP(3,__SC_ARGS,const char __user *, filename, int, flags, umode_t, mode));
return ret;
}
SYSCALL_ALIAS(sys_open, SyS_open);
static inline long SYSC_open(__MAP(3,__SC_DECL,const char __user *, filename, int, flags, umode_t, mode))
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
再根据以下宏定义:
#define __MAP0(m,...)
#define __MAP1(m,t,a) m(t,a)
#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__)
#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__)
#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__)
#define __MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__)
#define __MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__)
#define __MAP(n,...) __MAP##n(__VA_ARGS__)
#define __SC_DECL(t, a) t a <<<used to separate the type and param
#define __TYPE_IS_LL(t) (__same_type((t)0, 0LL) || __same_type((t)0, 0ULL))
#define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a
#define __SC_CAST(t, a) (t) a <<<used to forced type change
#define __SC_ARGS(t, a) a <<<only use the param
#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long)) <<<used to asert
最终可以得到:
asmlinkage long sys_open(const char __user * filename, int flags, umode_t mode);
static inline long SYSC_open(const char __user * filename, int flags, umode_t mode);
asmlinkage long SyS_open(long filename, long flags, long mode))
{
long ret = SYSC_open((const char __user *)filename, (int)flags, (umode_t)mode);
__MAP3(__SC_TEST,const char __user *, filename, int, flags, umode_t, mode);
__PROTECT(3, ret,__MAP3(__SC_ARGS,const char __user *, filename, int, flags, umode_t, mode));
return ret;
}
SYSCALL_ALIAS(sys_open, SyS_open);
static inline long SYSC_open(const char __user * filename, int flags, umode_t mode)
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
可以看出,实际上该宏做了四件事:
1 声明系统调用接口“sys_open”
2 声明并实现了中间接口“SyS_open”
3 声明了中间接口“SYSC_open”,并由该宏定义之后的代码段实现
4 将“sys_open”和“SyS_open”指向同一位置
2 在文件<arch/arm/include/uapi/asm/unistd.h>中有如下定义:
#define __NR_open (__NR_SYSCALL_BASE+ 5)
该定义用于将系统调用“sys_open”以系统调用编号引出到用户空间。
3 在文件<arch/arm/kernel/calls.S>中有如下定义:
CALL(sys_open)
该定义是为了告知内核增加了一项系统调用。
4 在文件<arch/arm/include/asm/unistd.h>中有如下定义:
#define __NR_syscalls (380)
该定义是告知内核一共定义了多少系统调用;当增加新的系统调用时,务必将该数增加。
例子:增加四个系统调用
kernel space:
diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
index 141baa3..acabef1 100644
--- a/arch/arm/include/asm/unistd.h
+++ b/arch/arm/include/asm/unistd.h
@@ -15,7 +15,7 @@
#include <uapi/asm/unistd.h>
-#define __NR_syscalls (380)
+#define __NR_syscalls (384)
#define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0)
#define __ARCH_WANT_STAT64
diff --git a/arch/arm/include/uapi/asm/unistd.h b/arch/arm/include/uapi/asm/unistd.h
index af33b44..2ac0145 100644
--- a/arch/arm/include/uapi/asm/unistd.h
+++ b/arch/arm/include/uapi/asm/unistd.h
@@ -406,6 +406,10 @@
#define __NR_process_vm_writev (__NR_SYSCALL_BASE+377)
#define __NR_kcmp (__NR_SYSCALL_BASE+378)
#define __NR_finit_module (__NR_SYSCALL_BASE+379)
+#define __NR_CustomEvent_open (__NR_SYSCALL_BASE+380)
+#define __NR_CustomEvent_wait (__NR_SYSCALL_BASE+381)
+#define __NR_CustomEvent_signal (__NR_SYSCALL_BASE+382)
+#define __NR_CustomEvent_close (__NR_SYSCALL_BASE+383)
/*
* This may need to be greater than __NR_last_syscall+1 in order to
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index c6ca7e3..54cedd5 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -389,6 +389,10 @@
CALL(sys_process_vm_writev)
CALL(sys_kcmp)
CALL(sys_finit_module)
+/* 380 */ CALL(sys_CustomEvent_open)
+ CALL(sys_CustomEvent_wait)
+ CALL(sys_CustomEvent_signal)
+ CALL(sys_CustomEvent_close)
#ifndef syscalls_counted
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
#define syscalls_counted
diff --git a/fs/Makefile b/fs/Makefile
index 4fe6df3..c751b46 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -11,7 +11,7 @@ obj-y := open.o read_write.o file_table.o super.o \
attr.o bad_inode.o file.o filesystems.o namespace.o \
seq_file.o xattr.o libfs.o fs-writeback.o \
pnode.o splice.o sync.o utimes.o \
- stack.o fs_struct.o statfs.o
+ stack.o fs_struct.o statfs.o work_queue_test.o
ifeq ($(CONFIG_BLOCK),y)
obj-y += buffer.o bio.o block_dev.o direct-io.o mpage.o ioprio.o
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 84662ec..dc1605e 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -846,4 +846,8 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
unsigned long idx1, unsigned long idx2);
asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
+asmlinkage long sys_CustomEvent_open(long eventNum);
+asmlinkage long sys_CustomEvent_wait(long eventNum);
+asmlinkage long sys_CustomEvent_signal(long eventNum);
+asmlinkage long sys_CustomEvent_close(long eventNum);
#endif
work_queue_test.c
static long do_CustomEvent_open(long eventNum)
{
......
}
SYSCALL_DEFINE1(CustomEvent_open, long, eventNum)
{
return do_CustomEvent_open(eventNum);
}
......
user space:
work_queue_test1.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/unistd.h>
int CustomEvent_open(int flag)
{
return syscall(380, flag); <<<此处的380对应系统调用“NR_CustomEvent_open”的编号。
}
int main(int argc, char ** argv)
{
int i;
// if(argc != 2)
// return -1;
i = CustomEvent_open(atoi(argv[1]));
printf("%d\n", i);
return 0;
}