C++完成这个模块代码,与JAVA和其它语言都写着擅长的功能模块,但是他们中间是怎么连接的?
两条路。
第一条路叫库函数,或者说基于ABI的引用。
思路是,规定一种所有人都遵循的ABI,比如动态库或者静态库规范。这个规范规定了如何引出符号、如何找到对应的代码块等内容。
比如说,你写了一个模块,提供了一个超级复杂的函数叫add,作用是输入两个integer,返回它们的和。那么你就可以把它编译成静态库或者动态库——静态库其实就是函数名索引的机器码,将来拷贝到你的(编译后的)程序里面就好了;而动态库则有一个函数名列表、对应的机器码以及重定位信息,将来在你的程序运行时按照名字载入对应机器码就完了。
具体可以查阅Windows或者Linux下动态库(dll/so)文件格式相关文档。
当然,这样搞可能有些麻烦。比如c++为了支持重载(同名函数入参不同功能不同)喜欢在后台偷偷把函数名字换了,也就是把两个整数相加叫做add_i_i、两个浮点数相加叫add_f_f——这是信口杜撰的,实际更复杂一些,而且可能每个编译器都不一样。
问题就在这里:每个编译器都不一样了,那这样搞出来的库,还能链接到别的程序里面去吗?
所以,就有了extern “C”这个指示,意思就是指导编译器:这个函数别瞎重命名了,原样输出!
缺点是,函数重载这个支持自然也没了。
另一个问题是类。C语言里面可没类……
所以微软搞了个OLE(Object Linking and Embedding,对象连接与嵌入),后来又变成了COM、COM+……
这个技术的思路就是以市场占有率最高的“虚函数表”这种实现方式为标准、以IUnknown这个接口为所有对象的统一基类,提供QueryInterface 、 AddRef 和 Release方法——熟悉面向对象和C++的马上就能看出它的设计意图。
打通C++程序的互联之后,其他语言只需给它“套个壳子”(用某种语言写个适配层),就也能使用它了——包括C。
说的更具体点:python/php等语言都可以调用C语言搞的动态库(需要语言本身提供的特殊方法做一个自动转换,并不能直接使用;但这个转换几乎是透明的,所以在你看来仅仅执行了一句loadmoudle(xxx.dll),其中包含的函数就可以自由使用了);那么,只要用C++把COM相关接口包装一层、做一个类似extern “C”的转换,自然就可以在其他语言中使用了。
第二条路叫进程间通讯。
它的基本机制是共享内存、全局锁/信号量等等,一些OS也会直接提供Unix Domain Socket、管道等支持(其中unix domain socket其实就是只能本机通讯的、不走网卡的、经专门优化效率爆棚的标准TCP/UDP;而管道其实是自动替换了通讯双方的标准输入stdin/标准输出stdout)。
然后,有一些人会继续封装,把上述机制封装的更好用,于是就有了消息队列(如RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMq等)、远程过程调用RPC(Remote Procedure Call)甚至数据库(没错,借用数据库交换信息,特定需求下可能成本最低、效率反而最高:必要时,你可以把数据库搞到内存里,也就是‘内存数据库’)等等。
具体采用哪种方式,需要根据项目规模、人员素质、性能要求、部署形式等等综合考虑——或许,某些情况下,直接使用静态库是成本最低、效率最高的做法;但另一些情况下,使用更为重量级的、现成的消息队列、RPC框架甚至数据库,反而更能降低开发成本、提高应用性能。
来自:https://www.zhihu.com/question/549930908/answer/2659152111
原理上有两种:
- 同一进程融合
- 跨进程融合
主要区别在于通信是如何实现的。
同一进程融合
两个或者多个语言完成的模块在同一个操作系统进程中运行。这种情况一般是通过 foreign function interface (FFI) 实现。基本上就是把两个语言写成的程序,编译到一起。比如 Python 可以调用 C 或者 Rust 写成的函数(二进制),通常 Python 会把计算密集的模块用 C 语言实现,比如 Numpy,然后再同一个进程里面调用它们,以提高计算性能。
同一进程的通讯可以直接通过共享虚拟内存空间实现,通讯性能最好。
跨进程融合
跨进程融合的灵活度是最高的,因为程序跑在不同的进程里面,这些进程可以是任何语言实现的,C、Java、Python、Rust、Lisp、Ocaml,任何语言。
弊端在于,由于无法共享同一虚拟内存空间,通讯相对于同一进程来说比较复杂。
- 内存映射 – 实际上还是共享物理内存
- Socket – 套接字
- 消息队列 – 用第三个进程来实现通讯
- 操作系统还提供一些底层的信号传递功能 – 信号
上面是实现二进制信息传递的方法,当然由于进程实现的语言和内存模型通常也是不一样的,在这个过程中我们可能还需要通讯协议,即确保两个进程对同一个二进制流的诠释是一样的,这通常会涉及一个序列化和反序列化的过程,因此通讯成本比单进程要高。
综上,根据项目的具体情况,我们可以选择不同的协同编程方案。总体来说:同进程融合性能最好,但是灵活度差;跨进程融合最灵活,但是性能会受到不同程度的损伤。
1、如果是C++的话,可以把C++封装成C语言函数接口,这样基本上可以提供给任何语言调用。这种就是直接整合。
2、可以使用socket,通过rpc,http等各种TCP协议通过网络通讯连接,这种是间接的,但是可以跨机器,跨地域远程调用。
3、可以利用消息组件,一端发消息给另一端,这个跟2也不太一样,消息可以订阅/发布、广播等等。
4、可以利用redis等进行内存数据交换,进行数据整合连接。
5、可以利用脚本胶水语言进行间接连接。
转载请注明:落伍老站长 » 两种编程语言是怎么一起连接工作的?