0

从 Erts 中学的 C 技巧

 2 years ago
source link: https://www.ttalk.im/2019/10/learn-some-c-skills-from-erts.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在对Erlang运行时代码进行分析时,发现了一些C语言不常见的语法应用。

因为对Erlang的OTP 17.0做了一段时间的代码分析,并且近期看到了大神写的书The Erlang Runtime System 。发现了Erts中的erl_emu.c的process_main有个C语言写法自己从没用过,就查阅了相关资 料。

跳转标签作为值1

简单说就是在函数内定义的标签,可以使用操作符‘&&’来进行取值,值的类型是void*,这 个值是一个定值,是不可以改变的。然后可以使用goto语句进行跳转。代码如下

void *ptr;
/* … */
ptr = &&foo;
goto *ptr;

static void *array[] = { &&foo, &&bar, &&hack };
goto *array[i];

在Erlang中,这种模式被用来完成Erlang的Beam指令流转,做了一个简单的模拟代码

#include<string.h>      
#include<stdlib.h>       
#include<stdio.h>                                                                                                      

typedef unsigned long Uint;  
typedef unsigned long  BeamInstr;   
typedef unsigned long  UWord;                                                                                          

#define OpCase(OpCode)    lb_##OpCode 
#define Goto(Rel) goto *((void *)Rel)   
#define OpCode(OpCode)  (&&lb_##OpCode)                                                                             
int main(){                                                
    BeamInstr* I;                                                                         
    BeamInstr* next; 
    BeamInstr beam_apply[2];   
     
    beam_apply[0]             = (BeamInstr) OpCode(i_apply);   
    beam_apply[1]             = (BeamInstr) OpCode(normal_exit);   
     
    printf("beam_apply %p\r\n",beam_apply);    
    printf("beam_apply[0] %p\r\n",beam_apply[0]);  
    printf("beam_apply[1] %p\r\n",beam_apply[1]);                                                                     
    I = (BeamInstr *) beam_apply;  
    next = (BeamInstr *) *I;   
    printf("next: %p\r\n",next);   
    Goto(next);
    OpCase(i_apply):{        
        printf("i_apply %p %p \r\n",I,(*I)); 
        I = I + 1;                          
        Goto(*I);                                                                           
    }
    OpCase(normal_exit):{  
        printf("normal_exit %p %p \r\n",I,(*I));  
        return 0;     
    }         
    return 1;          
}

beam_apply这个数组中存放的是被转化成整形数值的地址。赋值给next的时候,next类型是 指针,并且指向相应lb地址。 next = (BeamInstr *) *I; gcc会吧switch编译成jmp语句, 为什么还要使用这种费劲的方式而不使用switch呢?这是因为switch在jmp前需要进行一次 判断,而使用这种JUMP TABLE的模式是直接jmp到后面的地址。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK