Linux学习|Gentoo/Arch/FreeBSDLinuxLinux学习之路

汇编实现的memcpy和memset

2019-12-16  本文已影响0人  扫帚的影子

天天山珍海味的吃,也会烦。偶尔来点花生,毛豆小酌一点,也别有一番风味。

天天java, golang, c++, 咱们今天来点汇编调剂一下,如何?

通过这篇文章,您可以了解过:

闲话不多说,今天来看看汇编中如何实现memcpymemset(脑子里快回忆下你最后一次接触汇编是什么时候......)

函数是如何被调用的
栈的简单介绍

不管是写Windows程序还是Linux程序,也不管是用什么语言来写程序,我们经常会把某个独立的功能抽出来封装成一个函数,然后在需要的地方调用即可。看似简单的用法,那它背后是如何实现的呢?一般分为四步:

函数调用规则

函数一般都会有多个参数,我们根据函数调用时,

这两点(其实还有一点,就是代码被编译后,生成新函数名的规则,跟我们这里介绍的关系不大)来分类函数的调用方式:

glibc中的memcpy

我们先来看下glibc中的memcpy , 原型如下:

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

从src拷贝连续的n个字节数据到dest中, 不会有任何的内存越界检查。

char dest[5] = {0};                                                                                                    
char test[5] = {0,'b'};                                                                                                
char src[10] = {'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'};           

 ::memcpy(dest, src, 6);     
                                                   
std::cout << src << std::endl;  
std::cout << dest << std::endl;                                                                                        
std::cout << test << std::endl;

大家有兴趣的话可以考虑下上面的代码输出是什么?

汇编实现的memcpy

说来惭愧,汇编代码作者本人也不会写。不过我们可以参考linux源码里面的实现,这相对还是比较权威的吧。

它的实现位于 arch/x86/boot/copy.S, 文件开头有这么一行注释Copyright (C) 1991, 1992 Linus Torvalds, 看起来应该是大神亲手写下的。我们来看一看

GLOBAL(memcpy)
    pushw   %si
    pushw   %di
    movw    %ax, %di
    movw    %dx, %si
    pushw   %cx
    shrw    $2, %cx
    rep; movsl
    popw    %cx
    andw    $3, %cx
    rep; movsb
    popw    %di
    popw    %si
    retl
ENDPROC(memcpy)
glibc中的memset

我们先来看下glibc中的memset, 原型如下:

void *memset(void *s, int c, size_t n);

这个函数的作用是用第二个参数的最低位一个字节来填充s地址开始的n个字节,尽管第二个参数是个int, 但是填充时只会用到它最低位的一个字节。

你可以试一下下面代码的输出:

int c = 0x44332211;                                                                                                    
int s = 0;                                                                                                             
::memset((void*)&s, c, sizeof(s));                                                                                       
std::cout << std::setbase(16) << s << std::endl; // 11111111
汇编实现的memset

我们还是来看一下arch/x86/boot/copy.S中的实现:

GLOBAL(memset)
    pushw   %di
    movw    %ax, %di
    movzbl  %dl, %eax
    imull   $0x01010101,%eax
    pushw   %cx
    shrw    $2, %cx
    rep; stosl
    popw    %cx
    andw    $3, %cx
    rep; stosb
    popw    %di
    retl
ENDPROC(memset)

imull $0x01010101,%eax这句话就是把0x11变成0x11111111

好了,到这里这次的内容就结束了,有疏漏之处,欢迎指正。

上一篇 下一篇

猜你喜欢

热点阅读