内核是如何运行ko文件的–系统调用
现在我们己经知道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)
修改文章来源:https://www.toymoban.com/news/detail-791380.html
#undef __NR_syscalls
#define __NR_syscalls (__NR_helloworld+1)
文章来源地址https://www.toymoban.com/news/detail-791380.html
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;
}
到了这里,关于内核是如何运行ko文件的--系统调用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!