从内核代码调用系统调用

时间:2019-03-14 06:26:34

标签: c gcc linux-kernel

我正在尝试创建一种机制来读取进程的性能计数器。我希望从内核(版本4.19.2)本身执行此机制。

我可以从用户空间进行sys_perf_event_open()系统调用,如下所示。

syscall (__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);

我想从内核空间调用此调用。我从这里How do I use a Linux System call from a Linux Kernel Module

有了一些基本想法

这是我为达到此目的而采取的步骤:

  1. 为确保内核的虚拟地址保持有效,我使用了set_fs()get_fs()get_fd()

  2. 由于/include/linux/syscalls.h中定义了sys_perf_event_open(),因此我已将其包含在代码中。

最终,调用系统调用的代码如下所示:

mm_segment_t fs;
fs = get_fs();
set_fs(get_ds());
long ret =  sys_perf_event_open(&pe, pid, cpu, group_fd, flags);
set_fs(fs);

即使采取了这些措施,我仍然得到一个错误,声称​​“函数'sys_perf_event_open'的隐式声明” 。为什么已经包含定义它的头文件,为什么会弹出呢?是否有某种方式应该从内核代码中调用系统调用?

4 个答案:

答案 0 :(得分:1)

通常(不是特定于Linux),为系统调用完成的工作可以分为三类:

  • 从用户上下文切换到内核上下文(然后在返回路径上再次切换)。这包括更改处理器的特权级别,弄乱gs,摆弄堆栈以及减轻安全性(例如针对Meltdown)。这些东西很昂贵,如果您已经在内核中,那么它们是无用的和/或危险的。

  • 使用“函数编号”参数找到要调用的正确函数,然后调用它。这通常包括一些健全性检查(功能是否存在?)和表查找,以及用于处理所需输入和输出参数的代码,因为用于系统调用的调用约定(在用户空间中)与调用约定不同。正常使用C函数。这些东西很昂贵,如果您已经在内核中,那么它们是无用的和/或危险的。

  • 最终被调用的最终普通C函数。这是您可能直接使用的功能(请参见注释),而无需使用任何昂贵,无用和/或危险的系统调用垃圾。

注意:如果不使用系统调用的任何部分就不能直接调用最终的普通C函数(例如,如果最终的普通C函数未暴露于其他内核代码);然后您必须确定原因。例如,它可能不会公开,因为它会更改用户空间状态,并且从内核调用它会破坏用户空间状态,因此它不会公开/导出到其他内核代码,因此不会有人意外破坏所有内容。再举一个例子,也许没有理由不将其公开给其他内核代码,而您只需修改其源代码即可公开/导出。

答案 1 :(得分:1)

不建议使用sys_ *接口从内核内部调用系统调用,原因是已经提到了其他原因。在x86_64(我想这是您的体系结构)的特殊情况下,从内核版本v4.17开始,现在不使用此类接口(除少数例外)是一个严格的要求。可以在此版本之前直接调用系统调用,但是现在您看到的错误弹出(这就是为什么在网络上有很多使用sys_ *的教程的原因)。 Linux文档中建议的替代方法是在syscall和实际syscall的代码之间定义一个包装,该包装可以在内核中作为任何其他函数调用:

int perf_event_open_wrapper(...) {
    // actual perf_event_open() code
}

SYSCALL_DEFINE5(perf_event_open, ...) {
    return perf_event_open_wrapper(...);
}

来源:https://www.kernel.org/doc/html/v4.19/process/adding-syscalls.html#do-not-call-system-calls-in-the-kernel

答案 2 :(得分:0)

我们正在谈论哪个内核版本?

无论如何,您可以通过查看系统映射文件来获取sys_call_table的地址,或者如果已将其导出,则可以在找到地址后查找符号(请查看kallsyms.h)。在syscall表中,您可以将其视为空指针数组(void **),并找到所需的索引函数。即sys_call_table[__NR_open]是开放地址,因此您可以将其存储在void指针中,然后调用它。

编辑:您要做什么,为什么不调用syscalls就不能这样做?您必须了解,系统调用是内核对用户域的API,并且不应在内核内部真正使用它,因此应避免这种做法。

答案 3 :(得分:0)

  

从内核代码调用系统调用

(我主要回答的是标题;总结:禁止甚至认为

我不了解您的实际问题(我觉得您需要在您的问题尚不明确并且缺乏很多有用的动机和背景的情况下对它进行更多的解释)。但是,遵循Unix philosophy-的一般建议是最小化您的内核或内核模块代码的大小和漏洞范围,并尽可能方便地在用户中逐出此类代码,内核代码需要进行一些系统调用后,尤其是在systemd的帮助下登陆。您的问题本身就是对大多数Unix和Linux文化规范的违反。

您是否考虑过使用有效内核进行用户土地通信,尤其是netlink(7)socket(7)。也许你 需要一些特定于驱动程序的kernel thread

我的直觉是(在启动时从systemd开始的某个用户土地守护程序中)socket(2)AF_NETLINK非常适合您(无法解释的)需求。并且eventd(2)也可能是相关的。

但是,想到从内核内部使用系统调用会触发大脑中闪烁的红灯,我倾向于认为这通常是对操作系统内核的主要误解的征兆。请花些时间阅读Operating Systems: Three Easy Pieces,以了解操作系统原理。