22 memcpy 的调试
同样是一个 很常用的 glibc 库函数不管是 用户业务代码 还是 很多类库的代码, 基本上都会用到 内存数据的拷贝不过 我们这里是从 具体的实现 来看一下它的实现 主要是使用 汇编 来进行实现的, 因此 理解需要一定的基础。
前言
同样是一个 很常用的 glibc 库函数
不管是 用户业务代码 还是 很多类库的代码, 基本上都会用到 内存数据的拷贝
不过 我们这里是从 具体的实现 来看一下
它的实现 主要是使用 汇编 来进行实现的, 因此 理解需要一定的基础
测试用例
就是简单的使用了一下 memcpy, memset, memcmp
#include "stdio.h"
int main(int argc, char** argv) {
int x = 2;
int y = 3;
int z = x + y;
void *p1 = malloc(20);
void *p2 = malloc(20);
void *p3 = malloc(20);
printf("p1 : 0x%x\n", p1);
printf("p2 : 0x%x\n", p2);
printf("p3 : 0x%x\n", p3);
memset(p1, 'a', 12);
memcpy(p2, p1, 17);
int p1CmpResult = memcmp(p1, p2);
printf(" x + y = %d, p1CmpResult = %d\n ", z, p1CmpResult);
}
memcpy 的实现
memcpy 的实现是在 memcpy.S 中
但是通过调试发现, 类库的很多 memcpy 的调用都是走的这个 memcpy.S
但是 用户代码中的 memcpy 却没有进入这里的断点, 呵呵 这个就是一些 我们常规的理解 和 实际运行时存在差异的一个体现了, 调试是 最准确的能够说明确实执行的这里的代码的证据

上面 memcpy 的调用, 在 main 中的代码如下
可以看到是调用 "printf(" x + y = %d, p1CmpResult = %d\n ", z, p1CmpResult);" 的时候, 用户代码调用了 printf 函数, 然后 printf 的实现中具体调用了 memcpy 来处理业务 [这里主要是复制第一个 占位符之前的 常量字符串部分 " x + y = "]

如果是需要拷贝的长度 大于等于 32 字节, 则走后面 1after 的逻辑, 待会儿来看, 这里先看 小于 32 字节的拷贝处理
道理大致如下, 32 以下的数字, 换为二进制的表示, 最大为 0b11111, 数字 0-31 的表示为 0/1个16 + 0/1个8 + 0/1个4 + 0/1个2 + 0/1个1
因此 这里就是如果有 1 个 1, 则拷贝 1 字节, 如果有 1 个 2, 则拷贝 2 字节, 如果有 1 个 4, 则拷贝 4字节, 如果有 1 个 8, 则拷贝 8 字节, 如果有 1 个 16, 则拷贝 16 字节
走到最后, 需求的 数量的数据均拷贝完成, 效率也是 最极限的效率


如果是待拷贝的字节数大于等于 32 字节
先拷贝源字节对其 对齐 8 字节的数据, 这里的 alignloop 就是循环处理 这多余的字节, 单字节循环处理
后面的 32try, 以 32 字节为单位来开始循环拷贝数据, 循环拷贝, 最终剩余的数据小于 32 字节, 跳转到上面 小于32字节的处理流程
后面的 大于1kb 的场景, 我们这里就不深究了


完
更多推荐




所有评论(0)