zoukankan      html  css  js  c++  java
  • 学 Win32 汇编[17]: 关于压栈(PUSH)与出栈(POP) 之一


    记得刚学多线程的时候, 碰到一个结构:
    //Delphi 的语法描述
    PContext = ^TContext;
    _CONTEXT = record
      ContextFlags: DWORD;
      Dr0: DWORD;
      Dr1: DWORD;
      Dr2: DWORD;
      Dr3: DWORD;
      Dr6: DWORD;
      Dr7: DWORD;
      FloatSave: TFloatingSaveArea;
      SegGs: DWORD;
      SegFs: DWORD;
      SegEs: DWORD;
      SegDs: DWORD;
      Edi: DWORD;
      Esi: DWORD;
      Ebx: DWORD;
      Edx: DWORD;
      Ecx: DWORD;
      Eax: DWORD;
      Ebp: DWORD;
      Eip: DWORD;
      SegCs: DWORD;
      EFlags: DWORD;
      Esp: DWORD;
      SegSs: DWORD;
    end;
    

    从这个结构中可以基本洞察多线程的基本原理:
    1、在切换到另一个线程之前, 先把当前线程在寄存器中的数据保存在这个结构;
    2、重新切回线程时, 再才这个结构中读出相关数据到寄存器, 从而继续运行...

    压栈、出栈也是类似的道理.

    一个程序包含若干子程序, 子程序中一般会有自己的参数或局部变量.
    在执行这个子程序前, 应该先把寄存器中的相关数据暂存一下(子程序也要使用寄存器), 这就是所谓的压栈(PUSH);
    等子程序执行完毕, 再把之前压到栈中的数据取回(而让程序继续执行), 这就是所谓的出栈(POP).

    什么是 "栈"?

    程序把内存划分了若干区域, 其中有 "全局数据区" 和 "局部数据区".

    全局数据所在的位置叫 "堆";
    局部数据(局部变量、局部常量、子程序参数)所在的位置叫 "栈", 也叫 "堆栈".

    对 "堆" 和 "栈", 前人给出了不同的使用规则:
    "堆" 中的数据一般是由下到上排列;
    "栈" 的数据则完全相反, 是由下到上排列.

    验证 "堆" 与 "栈" 不同的数据排列方式:
    ; Test17_1.asm
    .386
    .model flat, stdcall
    
    include    windows.inc
    include    kernel32.inc
    include    masm32.inc
    include    debug.inc
    includelib kernel32.lib
    includelib masm32.lib
    includelib debug.lib
    
    .data?
        GlobalVal1 dd ?
        GlobalVal2 dd ?
        GlobalVal3 dd ?
    .code
    
    main proc
        LOCAL LocalVal1:dword, LocalVal2:dword, LocalVal3:dword
        
        ;获取全局变量地址(地址是顺序递增的):
        PrintHex offset GlobalVal1  ;00403054
        PrintHex offset GlobalVal2  ;00403058
        PrintHex offset GlobalVal3  ;0040305C
        
        ;获取局部变量地址(地址是顺序递减的):
        lea eax, LocalVal1
        PrintHex eax                ;0012FFBC
        lea eax, LocalVal2
        PrintHex eax                ;0012FFB8
        lea eax, LocalVal3
        PrintHex eax                ;0012FFB4
        ret
    main endp
    end main
    

    压栈与出栈的顺序:
    .386
    .model flat, stdcall
    
    include    windows.inc
    include    kernel32.inc
    include    masm32.inc
    include    debug.inc
    includelib kernel32.lib
    includelib masm32.lib
    includelib debug.lib
    
    .data
        val1 dd 111
        val2 dd 222
        val3 dd 333
    .code
    main proc
        push val1
        push val2
        push val3
        ;压栈完毕, 接着出栈
        pop val1
        pop val2
        pop val3
        ;查看取回的数据:
        PrintDec val1  ;333
        PrintDec val2  ;222
        PrintDec val3  ;111
        ;怎么反了? 这就是常说的 "栈中的数据是先进后出"! 让后进的先出就好了.
        ret
    main endp
    end main
    

    根据 "栈" 先进后出的特点, 写一个变量换值的程序:
    ; Test17_3.asm
    .386
    .model flat, stdcall
    
    include    windows.inc
    include    kernel32.inc
    include    masm32.inc
    include    debug.inc
    includelib kernel32.lib
    includelib masm32.lib
    includelib debug.lib
    
    .data
        val1 dd 111
        val2 dd 999
    .code
    
    main proc
        push val1
        push val2
        pop val1
        pop val2
        ;现在 val1 和 val2 的值已经交换
        PrintDec val1  ;999
        PrintDec val2  ;111
        ret
    main endp
    end main
    

    如果仅是交换变量的值, 可以使用 XCHG 指令:
    ; Test17_4.asm
    .386
    .model flat, stdcall
    
    include    windows.inc
    include    kernel32.inc
    include    masm32.inc
    include    debug.inc
    includelib kernel32.lib
    includelib masm32.lib
    includelib debug.lib
    
    .data
        val1 dd 111
        val2 dd 999
    .code
    
    main proc
        ;xchg va1, val2 ;指令都不支持对两个变量直接操作, 需要用个寄存器中转下
        mov  eax, val1
        xchg eax, val2
        mov  val1, eax
        PrintDec val1   ;999
        PrintDec val2   ;111
        ret
    main endp
    end main
    

    根据上面的原理, 也可以方便写出一个翻转字符串的函数:
    ; Test17_5.asm
    .386
    .model flat, stdcall
    
    include    windows.inc
    include    kernel32.inc
    include    masm32.inc
    include    debug.inc
    includelib kernel32.lib
    includelib masm32.lib
    includelib debug.lib
    
    .data
        szText db 'Hello World!', 0
    .code
    
    main proc
        ;把字符串中的字符逐个压入栈中
        mov ecx, sizeof szText - 1  ;把字符串长度(将要反复的次数)给 ecx, 没包括结束记号
        xor esi, esi                ;清空 esi, 准备用作数组索引
    @@: movzx eax, szText[esi]      ;循环读出并压栈
        push eax
        inc esi
        loop @B
        
        ;从栈中逐个取出并写入字符串
        mov ecx, sizeof szText - 1
        xor esi, esi
    @@: pop eax
        mov szText[esi], al
        inc esi
        loop @B
        
        PrintString szText  ;!dlroW olleH
        ret
    main endp
    end main
    ;做这个程序也有更好的方案, 譬如用 movs
    

  • 相关阅读:
    Idea 找不到tomcat server 服务器选项
    idea永久使用
    Jax
    我的2014
    Mysql锁机制
    mysql 查询效率提升
    Java 静态工厂方法
    java 面试题(转)
    Java Semaphore
    Java CyclicBarrier
  • 原文地址:https://www.cnblogs.com/del/p/1708891.html
Copyright © 2011-2022 走看看