我正在研究 Linux 内核 4.19.2。我想在不使用 perf 或任何此类工具的情况下读取可用的性能计数器。
在用户空间中,我使用如下代码。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>
#define rdpmc(counter,low,high) \
__asm__ __volatile__("rdpmc" \
: "=a" (low), "=d" (high) \
: "c" (counter))
void test(){
printf(".");
}
static long
perf_event_open (struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
int ret;
ret = syscall (__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
return ret;
}
int
main ()
{
unsigned long values1, values2;
unsigned int fixed0, low, high;
struct perf_event_attr pe;
int fd, i;
fixed0 = (1 << 30);
memset (&pe, 0, sizeof (struct perf_event_attr));
pe.type = PERF_TYPE_HARDWARE;
pe.size = sizeof (struct perf_event_attr);
pe.config = PERF_COUNT_HW_INSTRUCTIONS;
pe.disabled = 1;
pe.exclude_kernel = 0;
pe.exclude_user = 0;
pe.exclude_hv = 0;
pe.exclude_idle = 0;
fd = perf_event_open (&pe, 0, -1, -1, 0);
if (fd == -1)
{
fprintf (stderr, "Error opening leader %llx\n", pe.config);
exit (EXIT_FAILURE);
}
for (i=1; i<=50; i++)
{
ioctl (fd, PERF_EVENT_IOC_RESET, 0);
ioctl (fd, PERF_EVENT_IOC_ENABLE, 0);
rdpmc (fixed0, low, high);
values1 = ((unsigned long) high << 32) + (unsigned long) low;
test();
rdpmc (fixed0, low, high);
values2 = ((unsigned long) high << 32) + (unsigned long) low;
ioctl (fd, PERF_EVENT_IOC_DISABLE, 0);
printf (" %lu\n", values2 );
}
close (fd);
}
现在我想在内核代码中实现同样的事情。
我的目标是,在每次上下文切换时获取事件的计数并将其存储在任务结构中。因此最终我希望从内核中的 context_switch() 函数调用计数器。如果我从内核执行此操作,那么我将具有始终在当前线程的上下文中运行的优势。
我认为大部分代码也可以从内核中重用。但我不确定 ioctl() 部分。
答案1
从技术上讲,可以在内核代码中调用 ioctl。它被称为ksys_ioctl().[*] 注意init/
唯一的其他代码是如何调用 ksys_ioctl() 的。温和地说,这是一件不寻常的事情。呼唤性能内核内部的 ioctl 听起来更可疑。
到开始理解为什么这是不寻常的,我想指出这ksys_ioctl()
仍然需要fd
争论。这是文件描述符的编号在当前任务的范围内(进程/线程)。
[编辑] 我的目标是,在每次上下文切换时获取事件的计数并将其存储在任务结构中。因此最终我希望从内核中的 context_switch() 函数调用计数器。
啊啊啊。
所以你做到了不是想要从内核调用 ioctl()。 (你不会有一个文件描述符来调用 ioctl() 。而我有不是相信您想在每次 context_switch() 期间开始将文件描述符打开到当前任务的文件表中。即使您之后关闭它们)。
此时,您正在询问如何实现 Linuxperf
子系统(较小版本)。子系统perf
拥有性能计数器。如果您想使用性能计数器没有对于子系统perf
,您应该首先禁用perf
内核中的支持。在此过程的后期,您应该花时间查看以下内容中的相关材料:SDM(或相关手册 - 我还没有检查哪些)。
ksys_*()
[*] 旁注:将指针传递给函数时存在限制。基本上他们期望__user
内存,而不是内核内存,除非你使用set_fs()
..
编写perf
代码将是另一回事。您可以轻松找到代码并开始查看,例如单击PERF_EVENT_IOC_RESET。除了特定的行之外,它还会为您提供一个源文件(和目录)供您查看,您还可以继续单击:-)。因此我认为这不是你的问题。