现在我们己经知道insmod命令做了什么事情,当我们使用insmod命令加载ko文件的时候,会调用系统调用init_module和finit_module。那什么是系统调用呢?
什么是系统调用
系统调用是操作系统扌是供给编程人员的接囗,当编程人员写程序时,因为上层应用不能直接操作硬件,所以就要利用系统调用接囗来请求操作系统的照务,如访问硬件。系统调用是和CPU架进行绑定的。和内核版本也有关系。
回到init_module和finit_module这俩个系统调用就是应用程序调用系统调用,内核就会执行运行ko文件的操作。
系统调用的流程
以为init_module例,原型为:
#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
syscall函数原型:
long int syscall(long int sysno,.....)
参数sysno为系统调用号,每个系统有一个唯一的系统调用号来标识对应的函数。
…是可变参数,是系统调用所以带的参数。
作用:根据系统调用号,调用相应的系统调用。
那__NR_init_module和个函数绑定了呢?打开include/uapi/asm-generic/unistd.h文件,找到以下代码:
/* kernel/module.c */ #define __NR_init_module 105 __SYSCALL(__NR_init_module, sys_init_module) #define __NR_delete_module 106 __SYSCALL(__NR_delete_module, sys_delete_module)
__SYSCALL将系统调用号与sys_init_module函数绑定。这里有一个规律,在用户空间
我们使用xxx函数,对应的系统调用的函数就是sys_xxx;
sys_init_module函数定义在kernel/module.h文件中。sys_init_module函数定义是一个宏
定义。如下图所示
SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs) { int err; struct load_info info = { }; err = may_init_module(); if (err) return err; pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n", umod, len, uargs); err = copy_module_from_user(umod, len, &info); if (err) return err; return load_module(&info, uargs, 0); } SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) { int err; struct load_info info = { }; err = may_init_module(); if (err) return err; pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags); if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS |MODULE_INIT_IGNORE_VERMAGIC)) return -EINVAL; err = copy_module_from_fd(fd, &info); if (err) return err; return load_module(&info, uargs, flags); }
我们来看一下这个宏定义。这个宏定义定义在:include/linux/syscall.h中。如下图所示:
#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__)
SYSCALL_DEFlNE3宏定义中的3代表3个参数。这里一共有6个宏定义,所以我们最多可以带6个参数。
__VA_ARGS__等价于void __user *, umod,unsigned long, len, const char __user *, uargs #define SYSCALL_DEFINE0(sname) \ SYSCALL_METADATA(_##sname, 0); \ asmlinkage long sys_##sname(void) #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__) #define SYSCALL_DEFINEx(x, sname, ...) \ SYSCALL_METADATA(sname, x, __VA_ARGS__) \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) #define __PROTECT(...) asmlinkage_protect(__VA_ARGS__) #define __SYSCALL_DEFINEx(x, name, ...) \ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ __attribute__((alias(__stringify(SyS##name)))); \ static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ __MAP(x,__SC_TEST,__VA_ARGS__); \ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ return ret; \ } \ static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))
向系统中添加一个系统调用
步骤
1.在内核源码中添加自己的服务,需要编译进入内核
#include <linux/kernel.h> #include <linux/syscall.h> SYSCALL_DEFINE0(helloworld){ printk("this is my syscall helloworld\n"); return 0; }
2.添加系统调用号
修改include/uapi/asm-generic/unistd.h
vi include/uapi/asm-generic/unistd.h
加入
#define __NR_helloworld 1080 __SYSCALL(__NR_helloworld, sys_helloworld)
修改
#undef __NR_syscalls #define __NR_syscalls (__NR_helloworld+1)
3.编译烧写
4.测试
#include <stdio.h> #include <sys/syscall.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #define __NR_helloworld 1080 int main(int argc,char *argv[]){ syscall(__NR_helloworld); printf("here\n"); return 0; }