相关疑难解决方法(0)

每个程序员应该了解的内存?

我想知道Ulrich Drepper 从2007年开始对每个程序员应该知道的内容有多少仍然有效.另外,我找不到比1.0更新的版本或勘误表.

optimization x86 memory-management cpu-architecture micro-optimization

145
推荐指数
3
解决办法
2万
查看次数

x86分页如何工作?

这个问题旨在填补关于该主题的良好免费信息的真空.

我相信一个好的答案将适合一个大的答案或至少在几个答案.

主要目标是为完整的初学者提供足够的信息,以便他们可以自己学习手册,并能够理解与分页相关的基本操作系统概念.

建议的指导方针:

  • 答案应该是初学者友好的:
    • 具体但可能简化的例子非常重要
    • 欢迎使用所示概念的应用
  • 引用有用的资源是好的
  • 我们欢迎操作系统如何使用分页功能
  • PAE和PSE的解释是受欢迎的
  • 我们欢迎对x86_64进行小规模的讨论

相关问题以及为什么我认为它们不是愚蠢的:

paging x86 virtual-memory

78
推荐指数
2
解决办法
3万
查看次数

为什么在x86_64 ABI中选择地址0x400000作为文本段的开头?

文件中.27它说文本段从0x400000开始.为什么选择这个特定的地址?有什么理由吗?相同的地址被选择在GNU ldLinux:

$ ld -verbose | grep -i text-segment
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
Run Code Online (Sandbox Code Playgroud)

这是令人惊讶的,因为这个地址在32位x86可执行文件中更大:

$ ld -verbose | grep -i text-segment
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); . = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;
Run Code Online (Sandbox Code Playgroud)

我读了这个问题,讨论为什么为i386选择了0x080xxxxx地址,但它没有解释x86_64的变化.在这个问题上很难找到任何解释.有人有线索吗?

linux memory x86-64 elf abi

32
推荐指数
1
解决办法
2259
查看次数

TLB未命中缓存未命中?

有人可以解释TLB(翻译后备缓冲区)未命中和缓存未命中之间的区别吗?

我相信我发现TLB指的是某种虚拟内存地址,但我并不清楚这实际意味着什么?

我理解当一块内存(缓存行的大小)被加载到(L3?)缓存中并且如果所需的地址没有保存在当前缓存行中时,会导致缓存未命中 - 这是缓存未命中.

performance caching operating-system cpu-architecture tlb

24
推荐指数
1
解决办法
2万
查看次数

使用自修改代码观察在x86上获取过时的指令

我被告知并且从英特尔的手册中读到可以将指令写入内存,但是指令预取队列已经获取了陈旧的指令并将执行那些旧的指令.我没有成功观察到这种行为.我的方法如下.

英特尔软件开发手册从第11.6节开始说明

对当前在处理器中高速缓存的代码段中的存储器位置的写入导致相关联的高速缓存行(或多个行)无效.此检查基于指令的物理地址.此外,P6系列和奔腾处理器检查对代码段的写入是否可以修改已经预取执行的指令.如果写入影响预取指令,则预取队列无效.后一种检查基于指令的线性地址.

所以,看起来如果我希望执行陈旧的指令,我需要有两个不同的线性地址引用相同的物理页面.所以,我将内存映射到两个不同的地址.

int fd = open("code_area", O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
assert(fd>=0);
write(fd, zeros, 0x1000);
uint8_t *a1 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_FILE | MAP_SHARED, fd, 0);
uint8_t *a2 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_FILE | MAP_SHARED, fd, 0);
assert(a1 != a2);
Run Code Online (Sandbox Code Playgroud)

我有一个汇编函数,它接受一个参数,一个指向我想要更改的指令的指针.

fun:
    push %rbp
    mov %rsp, %rbp

    xorq %rax, %rax # Return value 0

# A far jump simulated with a far return
# Push the …
Run Code Online (Sandbox Code Playgroud)

c x86 caching self-modifying

23
推荐指数
3
解决办法
2563
查看次数

Bounds检查64位硬件

我正在hacks.mozilla.org上阅读64位Firefox版本的博客.

作者说:

对于asm.js代码,增加的地址空间还允许我们使用硬件内存保护来安全地从asm.js堆访问中删除边界检查.收益非常显着:asmjs-apps上的8%-17%- * - arewefastyet.com上报告的吞吐量测试.

我试图了解64位硬件如何对C/C++进行自动边界检查(假设编译器支持硬件).我在SO中找不到任何答案.我找到了一篇关于这个主题的技术论文,但我无法理解这是怎么做到的.

有人可以在边界检查中解释64位硬件辅助吗?

c c++ x86-64 sandbox virtual-memory

18
推荐指数
1
解决办法
548
查看次数

如何解释 Xeon 处理器在具有顺序复制和分散存储的循环中性能不佳?

c++在某些英特尔至强处理器上运行以下代码时,我偶然发现了一个特殊的性能问题:

// array_a contains permutation of [0, n - 1]
// array_b and inverse are initialized arrays
for (int i = 0; i < n; ++i) {
  array_b[i] = array_a[i];
  inverse[array_b[i]] = i;
}
Run Code Online (Sandbox Code Playgroud)

循环的第一行按顺序复制array_aarray_b(预期很少有缓存未命中)。第二行计算array_b(许多缓存未命中,因为array_b是随机排列)的倒数。我们也可以将代码分成两个单独的循环:

for (int i = 0; i < n; ++i)
  array_b[i] = array_a[i];
for (int i = 0; i < n; ++i)
  inverse[array_b[i]] = i;
Run Code Online (Sandbox Code Playgroud)

我原以为这两个版本(单循环与双循环)在相对现代的硬件上的性能几乎相同。但是,在执行单循环版本时,某些 Xeon 处理器似乎非常慢。

您可以在下方看到以纳秒为单位n的挂机时间除以在一系列不同处理器上运行代码段的时间。出于测试目的,代码是使用 GCC 7.5.0 编译的,并-O3 -funroll-loops -march=native …

performance intel cpu-architecture cpu-cache amd-processor

14
推荐指数
1
解决办法
408
查看次数

X86 CPU如何将地址转换为IO文本缓冲区等IO?

如果我想访问位于地址的X86中的VGA文本缓冲区 0xb8000:

uint16_t *VGA_buffer = (uint16_t*)0xb8000;
Run Code Online (Sandbox Code Playgroud)

然后我索引变量VGA_buffer作为一个正常的阵列,即VGA_buffer[0],VGA_buffer[1]

但是,我在x86中读到了内存映射,其中列出的地址是物理地址.

我的问题是:

CPU如何访问此地址?CPU是否知道在代码中明确写入的任何地址是物理地址,并且不应通过地址转换机制(逻辑地址 - >虚拟地址 - >到物理地址)?

提前致谢.

c paging x86 memory-management

12
推荐指数
1
解决办法
525
查看次数

为什么在64位虚拟地址与物理地址(52位长)相比,4位短(48位长)?

在"低级编程:英特尔®64架构上的C,汇编和程序执行"一书中,我读到:

每个虚拟64位地址(例如,我们在程序中使用的地址)由几个字段组成.地址本身实际上只有48位宽; 它被符号扩展为64位规范地址.它的特点是它的17个左位是相等的.如果不满足条件,则在使用时立即拒绝该地址.然后借助特殊表将48位虚拟地址转换为52位物理地址.

为什么虚拟地址和物理地址之间的差异为4位?

assembly memory-management virtual-memory memory-address mmu

12
推荐指数
1
解决办法
2304
查看次数

当base + offset与基数不同时,是否存在惩罚?

这三个片段的执行时间:

pageboundary: dq (pageboundary + 8)
...

    mov rdx, [rel pageboundary]
.loop:
    mov rdx, [rdx - 8]
    sub ecx, 1
    jnz .loop
Run Code Online (Sandbox Code Playgroud)

还有这个:

pageboundary: dq (pageboundary - 8)
...

    mov rdx, [rel pageboundary]
.loop:
    mov rdx, [rdx + 8]
    sub ecx, 1
    jnz .loop
Run Code Online (Sandbox Code Playgroud)

还有这个:

pageboundary: dq (pageboundary - 4096)
...

    mov rdx, [rel pageboundary]
.loop:
    mov rdx, [rdx + 4096]
    sub ecx, 1
    jnz .loop
Run Code Online (Sandbox Code Playgroud)

对于第一个片段,在4770K上,每次迭代大约5个周期,对于第二个片段,每次迭代大约9个周期,然后是第三个片段的5个周期.它们都访问完全相同的地址,这是4K对齐的.在第二个片段中,只有地址计算跨越页面边界:rdx并且rdx + 8不属于同一页面,负载仍然是对齐的.如果偏移量很大,则会再次回到5个周期.

这种效果一般如何起作用?


通过ALU指令从加载路由结果,如下所示:

.loop:
    mov rdx, …
Run Code Online (Sandbox Code Playgroud)

performance x86 assembly micro-optimization

11
推荐指数
1
解决办法
456
查看次数