memcpy内部实现是如何工作的?

31
标准C函数'memcpy'是如何工作的?它需要将一个(较大)的RAM块复制到RAM中的另一个区域。由于我知道在汇编中不能直接从RAM移动到RAM(使用mov指令),所以我猜测它在复制时使用CPU寄存器作为中间内存?但是,它是如何复制的呢?通过块(它如何通过块复制?),通过单个字节(char),还是使用最大的数据类型(比如long long double,在我的系统上为12个字节)进行复制?
编辑:好像可以直接从RAM中移动数据到RAM,我不是汇编专家,我对汇编的所有了解都来自于这份文档(X86汇编指南),其中提到mov指令的部分说你不能从RAM移动到RAM。显然这不是真的。

这是特定于平台的。请指定一个平台。 - Oliver Charlesworth
我使用Linux,Mac和Windows(分别是32位,64位和32位),但我在使用Linux时提出了这个问题。 - PersonWithName
3个回答

26

视情况而定。一般来说,在单个周期内,你无法复制大于最大可用寄存器的任何内容,但这并不是现代机器的真正工作方式。实际上,你更加关注的是DRAM的特性而不是CPU正在执行什么操作。机器的内存层次结构将在以最快速度执行此复制时发挥关键的决定性作用(例如,您是否正在加载整个高速缓存行?相对于复制操作,DRAM行的大小是多少?)。某些实现可能选择使用某种矢量指令来实现memcpy。没有参考具体实现,它基本上是一个一位缓冲区的逐字节复制。

这是一篇有趣的文章,描述了一个人优化memcpy的冒险历程。主要的观点是,它总是会针对特定的架构和环境进行定位,基于可以廉价执行的指令。


1
对于特定情况,比如当i=1、2或4时,该怎么办?Buf是什么? - mohammadsdtmnd

15

memcpy 的实现高度特定于其所在的系统。实现通常是硬件辅助的。

内存到内存的 mov 指令并不罕见 - 至少从 PDP-11 时代以来,它们就已经存在了,当时可以编写这样的代码:

    MOV FROM, R2
    MOV TO,   R3
    MOV R2,   R4
    ADD LEN,  R4
CP: MOV (R2+), (R3+) ; "(Rx+)" means "*Rx++" in C
    CMP R2, R4
    BNE CP

这行被注释的代码大致相当于C语言中的

*to++ = *from++;

现代的CPU具有直接实现memcpy的指令:您可以使用特殊寄存器加载源地址和目标地址,调用内存复制命令,然后让CPU完成其余操作。


1
它们至少从PDP-11时代就存在了 - 远远更久。 - Jim Balter
1
@JimBalter 这一点我一点也不感到惊讶 :) - Sergey Kalinichenko
1
to和from是空指针,我认为您不能对空指针进行解引用。您是否需要先将它们强制转换为(unsigned char*)类型? - Rockstar5645
1
@Rockstar5645 汇编语言没有类型的概念,因此它可以愉快地解引用您传递为 void* 的任何地址。当然,如果您正在使用 C 编写实现,您必须将这些指针强制转换为您可以解引用的内容,例如 unsigned char* - Sergey Kalinichenko

8

memcpy 的一个简单实现如下:

 while (n--) *s2++ = *s1++;

然而,glibc通常会在汇编代码中使用一些巧妙的实现。memcpy调用通常是内联的。

在x86上,该代码检查大小参数是否为2的字面值倍数或4的倍数(使用gcc内置函数),并使用带有movl指令的循环(复制4个字节),否则调用常规情况。

常规情况下,使用快速块拷贝汇编,使用repmovsl指令。


2
但是s2和s1都是空指针,我以为你不能解引用空指针。 - Rockstar5645
1
@ouah - 为什么只在大小是4的倍数时使用movl,而不总是尝试使用movl?如果您必须复制总共50个字节,您不能使用12个movl和2个mov来复制吗? - joepol
1
@Rockstar5645 - 在此之前你必须进行强制类型转换,我相信ouah提到了这个:gcc memcpy implementation - joepol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接