之前写过一篇关于 memcpy函数面试的文章
几个简单的笔试题
里面的代码使用的是char指针来实现,今天我们来看看Linux下面的memcpy
函数,它的实现上还是有一些巧妙的。
void * memcpy(void * dest, const void *src, size_t n) { if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) { __memcpy_aligned_up ((unsigned long) dest, (unsigned long) src, n); return dest; } __memcpy_unaligned_up ((unsigned long) dest, (unsigned long) src, n); return dest; }
首先,函数会对参数判断地址是不是对齐的,如果是对齐的话,会调用对齐的内存拷贝函数,如果是不对齐的话,会调用不对其的函数。
正常情况下,地址都会是按4字节对齐的
看看__memcpy_aligned_up 函数
/* * Hmm.. Strange. The __asm__ here is there to make gcc use an integer register * for the load-store. I don't know why, but it would seem that using a floating * point register for the move seems to slow things down (very small difference, * though). * * Note the ordering to try to avoid load (and address generation) latencies. */ static inline void __memcpy_aligned_up (unsigned long d, unsigned long s, long n) { ALIGN_DEST_TO8_UP(d,s,n); n -= 8; while (n >= 0) { unsigned long tmp; __asm__("ldq %0,%1":"=r" (tmp):"m" (*(unsigned long *) s)); n -= 8; s += 8; *(unsigned long *) d = tmp; d += 8; } n += 8; DO_REST_ALIGNED_UP(d,s,n); }
我们直接看代码操作就很清楚了,是按照8个字节来做的偏移
我写了个测试程序,测试程序和内核代码有一些小出入,主要是我用的是devc++来写代码,unsigned long并不是8字节。
测试代码如下
#include #include void * cris_memcpy(void *out, const void *in, unsigned int length) { if (!(((unsigned long) out ^ (unsigned long) in) & 7)) { printf("aligned\n"); } if (out == NULL || in == NULL) { return NULL; } while (length) { *(char*)out++ = *(char*)in++; length = length - 1; } return out; } typedef unsigned long long usize8_t; static inline void __memcpy_aligned_up (usize8_t d, usize8_t s, long n) { if (!(((usize8_t) d ^ (usize8_t) s) & 7)) { printf("aligned\n"); } //n -= 8; printf("%d %d\n",n,sizeof(usize8_t)); while (n >= 0) { usize8_t tmp; tmp = *(usize8_t *) s; n -= 8; s += 8; *(usize8_t *) d = tmp; d += 8; } n += 8; } int main() { char a[20] = "happy new year"; char b[20]; char c[20]; cris_memcpy(&b, &a, strlen(a)); __memcpy_aligned_up(&c, &a, strlen(a)); printf("a=%s\n",a); printf("b=%s\n",b); printf("c=%s\n",c); return 0; }
代码输出
最后,如果觉得不错,大家顺手点个赞,转发就是对我最大的鼓励和支持!
长按识别二维码关注公众号