V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
James369
V2EX  ›  程序员

跨语言之间的调用,原理是什么?

  •  
  •   James369 · 2020-12-01 16:10:07 +08:00 · 6752 次点击
    这是一个创建于 1232 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如:
    java 通过 jni 调用 c/c++模块。
    python 通过 pybind11 调用 c/c++模块。

    这是一种基于编译器 /解释器的技术,还是什么二进制原理?
    26 条回复    2020-12-02 16:17:49 +08:00
    pursuer
        1
    pursuer  
       2020-12-01 16:19:18 +08:00 via Android
    按照对应平台的 abi 调用规则去调就可以了,有些比如 lua 的 c 接口倒是平台无关的
    janus77
        2
    janus77  
       2020-12-01 16:27:07 +08:00
    建议百度 jni 原理
    xx6412223
        3
    xx6412223  
       2020-12-01 16:27:13 +08:00
    对 Java 来说和 c 一样,就是动态加载链接库的过程,面试的时候基本能答出来 dlopen dlsym 就给过了
    TargaryenChen
        4
    TargaryenChen  
       2020-12-01 16:40:54 +08:00
    最近正好在做一个 JSI 相关的内容,JSI 是啥? 可以看下我写的这篇文章 https://www.hellohub.cn/react-native/
    简单来说就是在 js 引擎( v8 )和 native ( c++)之间的一个封装层。
    比方说 react-native 中就有 jsi,他做的事情就是在 native 侧向 js 中注入方法,然后在 js 侧调用注入的方法并进行传参,这就实现了 js -> c++的通讯。 同理可以利用 v8 提供的方法调用你在 js 侧 global 上注册的方法并传参,这就实现了 c++ -> js 的通讯。至于传递参数的数据类型在两侧一定是有差异的,v8 提供相应的数据类型比方说 v8::String 能够将 c++侧的 std::string 和 js 侧的 string 类型相互转换。
    James369
        5
    James369  
    OP
       2020-12-01 16:42:09 +08:00
    @janus77 百度就没意思了,一起讨论才能活跃论坛,还可以撞出火花。
    zunceng
        6
    zunceng  
       2020-12-01 16:43:32 +08:00
    当然是协议啊 从 ffi 到 rpc 约定好了大家都认的协议 不就能互相调用了么
    James369
        7
    James369  
    OP
       2020-12-01 17:07:54 +08:00
    @zunceng 没那么简单吧,应该分编译型语言 A 和解释型语言 B,那么有四种情况:
    A call B, B call A, A call A, B call B
    liusir
        8
    liusir  
       2020-12-01 17:08:32 +08:00
    大神们讨论,我来学习一下
    icexin
        9
    icexin  
       2020-12-01 17:55:32 +08:00   ❤️ 2
    拿 Python 举例吧,用一种易于理解的方式简单说一下。

    假设你想让 Python 能调用 c 标准库里面的 puts 来打印字符串,首先需要使用 c 语言编写一个 so 模块,这个模块里面有一个签名类似 PyObject* py_puts(PyObject *self, PyObject *args)这样的 c 函数。
    在这个函数内部使用 python 源代码提供的工具函数解析 args,从而获取 python 传递过来的字符串参数,转换成 c 的字符串类型 char*,再传递给 puts 来打印。然后在 python 约定的模块初始化函数里面把 py_puts 注册到 python runtime 里面,一旦这个模块被加载,之后在 python 代码里面就可以调用 py_puts 来打印字符串了。

    那你可能问 py_puts 这个 c 函数又是怎么被调用的?原理很简单,因为 cpython 的解释器是用 c 写的,当用户在脚本里面调用 py_puts 的时候,解释器通过查表得到了我们之前注册到 runtime 的函数指针,之后就是 c 函数调用 c 函数了。
    Seawalker
        10
    Seawalker  
       2020-12-01 18:03:19 +08:00 via Android
    同一进城下,都是操作内存中的指针来实现
    zunceng
        11
    zunceng  
       2020-12-01 19:06:13 +08:00
    @James369 你想的太复杂了 n 种语言 调用的放式复杂度就是 n^2 了
    实际操作大部分语言都是以 so 格式或者 在其基础上约定一种格式( jni )来调用 c 语言,能调用 c 语言后其他语言调用就简单多了, 那么前面提到的方式 就是一种协议 /约定。

    参考资料 https://en.wikipedia.org/wiki/Foreign_function_interface
    zunceng
        12
    zunceng  
       2020-12-01 19:13:59 +08:00
    @James369 更好理解 /更实用一些的跨语言调用, 用 rpc ( grpc/thirft/jsonrpc/...)来实现跨语言调用, 一组服务通过网络调用约定好的各自提供的接口。这也是一种协议 /约定
    kingfalse
        13
    kingfalse  
       2020-12-01 19:20:28 +08:00 via Android
    还记得第一次搞 jna 是为了 winring0 模拟按键去登录网银控,件做爬,虫,想想就后怕
    hhhsuan
        14
    hhhsuan  
       2020-12-01 19:22:56 +08:00
    java 和 python 解释器不都是用 C 写的吗? C 调用 C 有啥问题?
    DoctorCat
        15
    DoctorCat  
       2020-12-01 19:27:43 +08:00   ❤️ 1
    动态链接库具备了语言无关的这种特性。本质上是 OS 提供的机制。
    billlee
        16
    billlee  
       2020-12-01 21:35:06 +08:00
    实际上就是大家都按 C 的接口来调用
    nightwitch
        17
    nightwitch  
       2020-12-01 23:05:57 +08:00
    不同语言之间形成一些约定啊,最常见的就是约定为 C 的 ABI 。因为操作系统的 API 和库往往以 C 的 API/ABI 提供,所以一个编程语言要使用操作系统提供的功能就要实现与 C 语言的库的交互。
    Yourshell
        18
    Yourshell  
       2020-12-01 23:07:22 +08:00
    按照约定读写内存,猜的。
    icyalala
        19
    icyalala  
       2020-12-01 23:16:09 +08:00
    去 Google 搜 calling convention
    不同 CPU 架构不一样( x86 、x64 、arm ),不同语言不一样,即使同语言也有多种声明,比如 x86 的 stdcall, fastcall, cdecl.
    Jirajine
        20
    Jirajine  
       2020-12-01 23:21:53 +08:00 via Android
    调用就是 把数据传给另一个语言写的程序->执行该程序得到结果->取回该结果。
    无论是通过 abi 、rpc 还是 rest,本质上都是一回事。
    Mithril
        21
    Mithril  
       2020-12-01 23:51:48 +08:00   ❤️ 2
    实际上你举得这些例子都是虚拟机实现的功能,JVM 去加载了 C++的 DLL/SO,然后包了一层给虚拟机内的代码用。
    其实跨语言调用还有很多种,比如 Windows 之前的 COM 这种,它的实现是系统提供的功能。
    本质上跨语言调用要解决的问题就只有两个
    1. 如何定位并调用对方的函数。
    2. 双方的数据类型要如何转换。
    你像 Java 或者 Python 调用 C++,这两个问题是虚拟机帮你解决了。Windows 的 COM 则是系统帮你解决了一部分,IDL 帮你解决了一部分,虽然这技术当年大部分也只用在 C++上,而且现在已经没人用了。
    不过更多的则是用多进程的办法,然后通过 IPC 来处理。IPC 的方法就多了去了,但是核心上解决的也就是这两个问题。
    你可以参考一下 SWIG 这个项目,它可以让各种语言去调用 C++的代码。
    MrKrabs
        22
    MrKrabs  
       2020-12-02 03:25:09 +08:00
    dlopen
    tianyamomo
        23
    tianyamomo  
       2020-12-02 08:40:13 +08:00
    搞 cef 和 qt 的 web 控件的时候都是把 c++的方法注册到 js 的执行环境里面,整个 js 环境都是 c/c++写的这应该不难,其他的都应该差不多吧
    young1lin
        24
    young1lin  
       2020-12-02 10:20:30 +08:00
    我记得 Java 调用 DLL 也是有限制的,对应的 C++ 代码必须放在 JAVA_HOME/bin 下面好像。Java 虚拟机对这个 native 函数编译的时候就开始语法解析了。
    mingl0280
        25
    mingl0280  
       2020-12-02 16:06:03 +08:00 via Android
    Linux: dlopen(), dlsym(), dlclose()
    Windows: LoadLibrary(), GetProcAddress(), FreeLibrary()
    mingl0280
        26
    mingl0280  
       2020-12-02 16:17:49 +08:00 via Android
    底层实际上是操作系统提供的一系列动态加载可执行代码的操作。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3252 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 14:17 · PVG 22:17 · LAX 07:17 · JFK 10:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.