我想要一个库,允许对 Linux 可执行文件的关键部分进行“自我分析”。就像人们可以使用一个部分计时一样获取当日时间() http://linux.die.net/man/2/gettimeofday or RDTSC http://www.strchr.com/performance_measurements_with_rdtsc我希望能够对分支未命中和缓存命中等事件进行计数。
有许多工具可以做类似的事情(perf https://perf.wiki.kernel.org/index.php/Main_Page, PAPI http://icl.cs.utk.edu/papi/, likwid https://code.google.com/p/likwid/)但我还没有找到任何符合我要找的东西。 Likwid 最接近,所以我主要在寻找修改它现有的方法标记API https://code.google.com/p/likwid/wiki/LikwidPerfCtr#Using_the_marker_API.
每个核心计数器的值存储在 MSR(型号特定寄存器)中,但对于当前的 Intel 处理器(Sandy Bridge 以后),“非核心”测量(内存访问和与 CPU 整体相关的其他内容)可通过以下方式访问: PCI。
通常采取的方法是使用 MSR 来读取msr内核模块 http://man7.org/linux/man-pages/man4/msr.4.html,并且 PCI 计数器(如果支持)是从sysfs-pci https://www.kernel.org/doc/Documentation/filesystems/sysfs-pci.txt等级制度。问题是,这两个或这些都要求阅读器以 root 身份运行并具有“setcap cap_sys_rawio”。这对于许多用户来说很困难(或不可能)。
它也不是特别快。由于目标是分析小段代码,因此使用系统调用读取每个计数器的“偏差”很大。事实证明,普通用户可以使用 RDPMC 读取 MSR 寄存器。我还没有一个很好的解决方案来读取 PCI 寄存器。
一种方法是通过以 root 身份运行的“访问服务器”来代理所有内容。这可以工作,但会比使用 /proc/bus/pci 更慢(因此不太准确)。我试图找出如何最好地使计数器的 PCI“配置”空间对非特权程序可见。
我想到的最好办法是让服务器以 root 身份运行,客户端可以在启动时通过 Unix 本地域套接字连接到该服务器。作为 root,服务器将打开相应的设备文件,并且将打开的文件句柄传递给客户端 http://www.lst.de/~okir/blackhats/node121.html。然后,客户端应该能够在执行期间自行进行多次读取。有什么理由这行不通吗?
但即使我这样做,我仍然会使用pread() 系统调用 http://man7.org/linux/man-pages/man2/pread.2.html(或类似的东西)每次访问,其中可能有数十亿次。如果尝试对小于 1000 个循环部分进行计时,这可能会产生太大的开销。相反,我想弄清楚如何访问这些计数器内存映射 I/O http://en.wikipedia.org/wiki/Memory-mapped_I/O.
也就是说,我希望对内存中地址表示的每个计数器进行只读访问,I/O 映射发生在处理器和 IOMMU 级别,而不涉及操作系统。这在英特尔架构软件开发人员第 1 卷第 16.3.1 节内存映射 I/O http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-1-manual.pdf.
这似乎几乎是可能的。在 proc_bus_pci_mmap() 中/proc/bus/pci 的设备处理程序 https://github.com/mirrors/linux/blob/HEAD/drivers/pci/proc.c似乎允许映射配置区域,但仅限根用户,并且仅当我有 CAP_SYS_RAWIO 时。
static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
{
struct pci_dev *dev = PDE_DATA(file_inode(file));
struct pci_filp_private *fpriv = file->private_data;
int i, ret;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
/* Make sure the caller is mapping a real resource for this device */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
if (pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
break;
}
if (i >= PCI_ROM_RESOURCE)
return -ENODEV;
ret = pci_mmap_page_range(dev, vma,
fpriv->mmap_state,
fpriv->write_combine);
if (ret < 0)
return ret;
return 0;
}
因此,虽然我可以将文件句柄传递给客户端,但它不能 mmap() 它,而且我想不出任何方法与非后代进程共享 mmap 区域。
(最后,我们进入问题!)
因此,假设我确实希望在非特权进程中拥有一个指针,每次都可以从 PCI 配置空间中读取数据,而无需内核的帮助,我的选择是什么?
1)也许我可以让根进程打开/dev/mem,然后将该打开的文件描述符传递给子进程,然后子进程可以映射它想要的部分。但我想不出任何方法可以保证远程安全。
2)我可以编写自己的内核模块,它看起来很像linux/drivers/pci/proc.c,但省略了对通常权限的检查。由于我可以将其锁定,使其只读并且仅用于我想要的 PCI 空间,因此它应该相当安全。
3)??? (这就是你进来的地方)