详解memcpy的实现原理,可不只是逐个字节复制那么简单

目录

1.引言

2.基本定义

3.实现策略

4.核心实现


1.引言

  memcpy在C/C++开发中很常用,面试时候也会经常被问到memcpymemmove的区别,本文先介绍下memcpy的原理。

        可能各个平台的实现原理不同,但都大同小异,本文主要介绍glibc中的memcpy

2.基本定义

void *memcpy(void *dest, const void *src, size_t n);

memcpy的功能是将src指向的内存区域的前n个字节复制到dest指向的内存区域。

使用memcpy,需要注意以下几点:

  1. 不处理重叠区域(这是memmove的职责)

  2. 返回目标地址dest

  3. 要求调用者确保:

    1. destsrc都是有效指针

    2. 内存区域不重叠

    3. 有足够的空间

3.实现策略

glibcmemcpy实现采用了三层复制策略:

  1. 字节复制(Byte Copy)

  2. 字复制(Word Copy)

  3. 页复制(Page Copy)

4.核心实现

整体策略大体如下

  1. 数据量小于16字节:

    1. 直接使用字节复制

    2. 避免对齐开销

  2. 数据量在16字节到16KB之间(不一定是16KB,为了方便起见,统一使用16,你理解原理即可):

    1. 先对齐

    2. 使用字复制

    3. 处理剩余字节

  3. 数据量大于16KB:

    1. 先对齐

    2. 尝试页复制

    3. 字复制处理剩余数据

    4. 字节复制处理尾部

以下是glibc-2.10.1中memcpy函数的源码:

#include <string.h>
#include <memcopy.h>
#include <pagecopy.h>
 
#undef memcpy
 
void *
memcpy (dstpp, srcpp, len)
     void *dstpp;
     const void *srcpp;
     size_t len;
{
  unsigned long int dstp = (long int) dstpp;
  unsigned long int srcp = (long int) srcpp;
 
  /* Copy from the beginning to the end.  */
 
  /* If there not too few bytes to copy, use word copy.  */
  if (len >= OP_T_THRES)
    {
      /* Copy just a few bytes to make DSTP aligned.  */
      len -= (-dstp) % OPSIZ;
      BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ);
 
      /* Copy whole pages from SRCP to DSTP by virtual address manipulation,
	 as much as possible.  */
 
      PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len);
 
      /* Copy from SRCP to DSTP taking advantage of the known alignment of
	 DSTP.  Number of bytes remaining is put in the third argument,
	 i.e. in LEN.  This number may vary from machine to machine.  */
 
      WORD_COPY_FWD (dstp, srcp, len, len);
 
      /* Fall out and copy the tail.  */
    }
 
  /* There are just a few bytes to copy.  Use byte memory operations.  */
  BYTE_COPY_FWD (dstp, srcp, len);
 
  return dstpp;
}
libc_hidden_builtin_def (memcpy)

详细分析:

字节复制

最基础的复制方式,一次复制一个字节:

#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes)             \
do                        \
{                       \
  size_t __nbytes = (nbytes);               \
  while (__nbytes > 0)                  \
  {                     \
    byte __x = ((byte *) src_bp)[0];              \
    src_bp += 1;                    \
    __nbytes -= 1;                  \
    ((byte *) dst_bp)[0] = __x;               \
    dst_bp += 1;                    \
  }                     \
} while (0)

   使用场景:

  • 小数据量复制

  • 处理非对齐数据

  • 处理剩余字节

字复制

按机器字长进行复制,提高效率:

#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes)          \
  do                        \
    {                       \
      if (src_bp % OPSIZ == 0)                  \
  _wordcopy_fwd_aligned (dst_bp, src_bp, (nbytes) / OPSIZ);       \
      else                      \
  _wordcopy_fwd_dest_aligned (dst_bp, src_bp, (nbytes) / OPSIZ);        \
      src_bp += (nbytes) & -OPSIZ;                \
      dst_bp += (nbytes) & -OPSIZ;                \
      (nbytes_left) = (nbytes) % OPSIZ;               \
    } while (0)

优化特点:

  • 利用CPU字长进行批量复制

  • 要求内存对齐

  • 比字节复制更高效

页复制

最高效的复制方式,适用于大块数据:

#define PAGE_COPY_FWD_MAYBE(dstp, srcp, nbytes_left, nbytes)              \
  do {                                                                      \
    if ((nbytes) >= PAGE_COPY_THRESHOLD                                     \
        && PAGE_OFFSET ((dstp) - (srcp)) == 0)                             \
    {                                                                       \
      /* 处理第一个不完整页 */                                              \
      size_t nbytes_before = PAGE_OFFSET (-(dstp));                        \
      if (nbytes_before != 0)                                              \
        {                                                                   \
          WORD_COPY_FWD (dstp, srcp, nbytes_left, nbytes_before);          \
          nbytes -= nbytes_before;                                         \
        }                                                                   \
      /* 执行页复制 */                                                      \
      PAGE_COPY_FWD (dstp, srcp, nbytes_left, nbytes);                    \
    }                                                                       \
  } while (0)
  
#define PAGE_COPY_THRESHOLD      (16384)

#define PAGE_SIZE    __vm_page_size
#define PAGE_COPY_FWD(dstp, srcp, nbytes_left, nbytes)               \
  ((nbytes_left) = ((nbytes)                       \
          - (__vm_copy (__mach_task_self (),             \
              (vm_address_t) srcp, trunc_page (nbytes),   \
              (vm_address_t) dstp) == KERN_SUCCESS       \
             ? trunc_page (nbytes)                 \
             : 0)))
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值