API 与 ABI 的区别 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
kaiduo
V2EX    程序员

API 与 ABI 的区别

  •  
  •   kaiduo 2021-12-20 01:01:37 +08:00 3848 次点击
    这是一个创建于 1399 天前的主题,其中的信息可能已经有所发展或是发生改变。

    image

    原文链接: https://github.com/xiaoxiaojx/blog/issues/22

    背景

    Node-API 的基本概念里面提到了 ABI, 前端开发的同学对这个词语可能就比较陌生,和平时经常提到的 API 有什么区别?

    Node-API (以前称为 N-API )是用于构建原生插件的 API 。它独立于底层 Javascript 运行时(例如,V8 )并作为 Node.js 本身的一部分进行维护。此 API 将在 Node.js 的各个版本中保持稳定的应用程序二进制接口 (ABI)。它旨在将插件与底层 Javascript 引擎中的更改隔离开来,并允许为一个主要版本编译的模块无需重新编译即可在 Node.js 的后续主要版本上运行

    API 与 ABI

    API 应用程序接口

    这是从应用程序 /库公开的一组公共类型 /变量 /函数。 在 C/C++ 中,这是应用程序附带的头文件中公开的内容。

    ABI 二进制接口

    这就是编译器构建应用程序的方式。 它定义了事物(但不限于):

    • 如何将参数传递给函数(寄存器 /堆栈)
    • 谁从堆栈中清除参数(调用者 /被调用者)
    • 返回值放置的位置以供返回
    • 异常如何传播

    举个例子

    下面的 main.c 程序依赖了 mylib 这个库, mylib 这个库对外暴露了 mylib_init 这个接口, 该接口的出参与入参可以看 mylib.h 中的类型定义。

    // main.c #include <assert.h> #include <stdlib.h> #include "mylib.h" int main(void) { mylib_mystruct *myobject = mylib_init(1); assert(myobject->old_field == 1); free(myobject); return EXIT_SUCCESS; } 
    // mylib.c #include <stdlib.h> #include "mylib.h" mylib_mystruct* mylib_init(int old_field) { mylib_mystruct *myobject; myobject = malloc(sizeof(mylib_mystruct)); myobject->old_field = old_field; return myobject; } 
    // mylib.h #ifndef MYLIB_H #define MYLIB_H typedef struct { int old_field; } mylib_mystruct; mylib_mystruct* mylib_init(int old_field); #endif 

    现在 mylib 这个库进行了 v2 版本的升级。v2 版本修改了 mylib_mystruct 的定义, 新增加了 new_field 字段,新的定义如下

    // mylib.h typedef struct { int new_field; int old_field; } mylib_mystruct; 

    此时我们只重新编译 mylib, 不重新编译 main.c 主程序。然后运行 main.out, 发现 main 函数里面的 assert 错误了...

    // main.c assert(myobject->old_field == 1); 

    因为 myobject 还是访问的第一个字段, 但是现在第一个字段为 new_field 了,程序中并没有为它赋值。此时对于用户来说 API 没有造成 break change, 可以不用修改代码来适配。但是由于 ABI 的 break change 导致需要重新编译主程序,所以 ABI 的稳定性的维持是高于 API 的

    如果把新增 new_field 放在 old_field 之后了,发现程序运行是没有问题的。mylib 通过后者的方式去升级 v2 版本,即使新增了字段,ABI 依然是稳定的。

    // mylib.h typedef struct { int old_field; int new_field; } mylib_mystruct; 

    扩展阅读

    下面所示的使用 Node-API 开发的 c++ 插件的代码例子, 对于我来说就比较好奇 napi_value 的定义

    // demo napi_status status; napi_value object, string; status = napi_create_object(env, &object); if (status != napi_ok) { napi_throw_error(env, ...); return; } status = napi_create_string_utf8(env, "bar", NAPI_AUTO_LENGTH, &string); if (status != napi_ok) { napi_throw_error(env, ...); return; } status = napi_set_named_property(env, object, "foo", string); if (status != napi_ok) { napi_throw_error(env, ...); return; } 

    最后我们在 js_native_api_types.h 文件找到了 napi_value 的定义。napi_value 是 struct napi_value__ 类型的指针,其实 napi_value__ 是未定义的。从源码中的注释可知, 编译时 undefined structs 会比 void* 更加安全。

    // src/js_native_api_types.h // JSVM API types are all opaque pointers for ABI stability // typedef undefined structs instead of void* for compile time type safety typedef struct napi_value__* napi_value; 

    实测上面的 napi_value__ 是 undefined 编译是会通过的,实际使用的时候强制类型转换为目标类型即可。

    参考

    13 条回复    2021-12-21 02:20:02 +08:00
    12101111
        1
    12101111  
       2021-12-20 01:11:47 +08:00
    实践上并不存在真正稳定的 ABI ,对代码的任意修改,对编译器或编译器参数的修改都有可能导致代码无法执行
    只有细致的对比两个编译后的二进制才能得知是否发生了 ABI 变动
    https://cor3ntin.github.io/posts/abi/
    xupefei
        2
    xupefei  
       2021-12-20 02:01:42 +08:00 via iPhone
    谈 ABI 不能靠代码,得把反汇编放出来。讲一下导出表和 call 指令一下就懂了,不用费这么多口舌。
    jones2000
        3
    jones2000  
       2021-12-20 08:01:07 +08:00
    直接用 json 字符串作为参数不就可以了。
    whi147
        4
    whi147  
       2021-12-20 08:58:21 +08:00 via iPhone
    符号文件链接不上
    2i2Re2PLMaDnghL
        5
    2i2Re2PLMaDnghL  
       2021-12-20 09:20:31 +08:00
    @jones2000 这个就叫『序列化』,正是一种通用的解决各种不兼容的方法。甚至 API 不兼容也没问题,golang 写的服务器你也可以拿 Python 去请求。序列化也不仅仅是 json ,也有 xml ,x-www-form-urlencoded ,msgpack ,甚至古早的 s expr 。
    不过,具体地说,它也依赖于一致的『字符序列』接口。大部分与 CJK 打交道的程序员应该都遇到过编码不一致的问题(手持两把锟斤铐,嘴上直呼烫烫烫)。也有字符串长度如何表示(臭名昭著的所谓「粘包」)、编码大小端等问题。反序列化过程中造成的漏洞也不少。
    KaynW
        6
    KaynW  
       2021-12-20 13:07:44 +08:00
    放个 arcane 的图干嘛
    jones2000
        7
    jones2000  
       2021-12-20 13:20:18 +08:00
    @2i2Re2PLMaDnghL 编码或粘包问题, 只要是用到了字符串作为变量都会有, 就算你是用结构体里面放一个字符串指针也一样。漏洞的问题,是个程序都会有漏洞,跟用什么接口方式无关。
    如果是方便开发就用 json ,特别是跨语言,跨平台,空间和运算成本大一点,毕竟是要序列化的。
    kaiduo
        8
    kaiduo  
    OP
       2021-12-20 23:42:59 +08:00
    @KaynW 文章的精髓所在 ~
    kaiduo
        9
    kaiduo  
    OP
       2021-12-20 23:43:58 +08:00
    @12101111 Node-api 说是会维持稳定的 ABI ,还是实践比较少
    kaiduo
        10
    kaiduo  
    OP
       2021-12-20 23:46:52 +08:00
    @jones2000 所以说 Js 开发的同学就比较陌生 ABI 这个概念,清一色的 API
    kaiduo
        11
    kaiduo  
    OP
       2021-12-20 23:47:32 +08:00
    @jones2000 所以说 Js 开发的同学就比较陌生 ABI 这个概念,遇见的都是 API
    kaiduo
        12
    kaiduo  
    OP
       2021-12-20 23:50:54 +08:00
    @xupefei 学习了,看来我这里只是很浅显的理解
    ungrown
        13
    ungrown  
       2021-12-21 02:20:02 +08:00
    @2i2Re2PLMaDnghL #5
    UTF-8 默秒全
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2564 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 02:30 PVG 10:30 LAX 19:30 JFK 22:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86