内核是如何运行ko文件的--系统调用

简介: 内核是如何运行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)

修改

#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;
}


目录
相关文章
|
5月前
|
Linux
linux 系统调用打印功能
linux 系统调用打印功能
26 0
|
10月前
|
Linux 编译器
内核是如何运行ko文件的--insmod命令
内核是如何运行ko文件的--insmod命令
431 0
|
Linux 测试技术
linux内核探索--系统调用(传参)
linux内核探索--系统调用(传参)
136 0
linux内核探索--系统调用(传参)
|
Linux 开发工具