非 c 语言如何与操作系统交互?
Posted
技术标签:
【中文标题】非 c 语言如何与操作系统交互?【英文标题】:How do non c languages interact with operating system? 【发布时间】:2014-12-05 11:44:38 【问题描述】:在linux上(例如),我们可以直接使用OS提供的api(open/close/read/write)进行系统调用,也可以在C中使用libc提供的函数(fopen等)。
在其他语言中是如何实现的?
【问题讨论】:
见Wikipedia: Application Programming Interface (API)和Wikipedia: application binary interface (ABI)和Wikipedia: Language binding对关键术语的描述 【参考方案1】:在其他语言中,这通常(总是?)通过围绕本机函数调用的包装器来实现。实际上,它看起来像这样。 LanguageX -> Wrapper -> 本机代码(C/C++)
有时此包装器采用整个框架的形式,例如在 JNI 中用于 java。 这也适用于 Groovy 脚本。
使用某些语言,您可以以类似于 C/C++ 的方式将本机 DLL 直接加载到您的应用程序中。在 Python 使用 ctypes 的情况下确实如此。
其他时候,根本不需要本地调用,并且可以直接使用您选择的语言产生相同的结果。跨平台 GUI 工具包是一个可以完全替换本地操作系统调用的小例子。比如用 GTK 或 QT
希望能把事情搞清楚 :) 如果您想要任何其他特定于语言的详细信息,请告诉我。
【讨论】:
我不认为 Python 比 Java 更接近 C。 谢谢,我只是说它更接近 C,因为你可以直接加载本机 dll。为澄清而编辑。 谢谢。这就是我一直在寻找的。不过,了解 wrapper 究竟是如何工作的会很有趣。 这将是相当深入的,稍微超出了问题的范围。见Wrapper library。在这种情况下,包装库将是某种形式的本机接口。 kostix 的回答描述了这一切在内部是如何工作的,而我的回答更多的是从用户的角度进行概括。【参考方案2】:您最初的前提是错误的:在 Linux 上,当您在 C 代码中调用 open(2)
时,您不是进行系统调用,而是调用 glibc
提供的函数(或您的程序碰巧使用的 C 标准库的任何实现——还有其他的,例如μLibc、dietlibc 等),并且该函数的实现实际上使系统调用——正确地进行必要的设置以符合到目标架构的ABI 约定。
您可以在 C 中直接进行系统调用,但它看起来与仅调用 open()
大不相同;一些指针是this 和this。
回顾一下:the POSIX standard(指定open(2)
和朋友的外观和行为方式)只指定了一个相当高级的接口(就 C 编程语言而言),但实现该标准的具体内核具有他们自己的系统调用集和关于如何调用它们的约定(在同一内核的 H/W 架构之间有所不同),它是 C 标准库,它“知道”如何调用一个具体的内核来确保 C 程序调用 POSIX API 最终会使用经过适当设置的适当系统调用。换句话说,在 Linux 上运行的典型 C 应用程序中,您并没有直接处理系统调用——您只是有一个印象。
其他语言/运行时基本上有两种选择:
依赖目标平台的libc
并依赖它在它们和内核之间进行调解。
直接实现调用目标内核必要的系统调用。
我所知道的大多数语言/运行时都采用第一条路线(Python、Java 等等),而其中一些采用另一条路线——例如,Go、Free Pascal 的参考实现。
2014-10-13 更新回答@tan
评论中的问题:
那么,如果采用第一条路由(libc),如何从python/java调用libc函数?
基本上,该方法类似于系统调用的方法:再次调用 C,对于硬件体系结构和 C 编译器的每种组合都是标准化的。请参阅this discussion 了解更多信息和更多指示。
请注意,实际上,大多数能够与 C 代码交互的软件都使用以下两种方法之一:
它本身是用 C 语言编写的,因此在外部 C 代码中实现对动态或静态链接的调用由编译调用软件的编译器处理。
也就是说,您的软件必须关心的唯一事情是正确地安排其数据由 C 端操作。一个例子是确保传递给 C 端的内存块在 C 端完成之前不会被垃圾回收。
Go 编程语言的cgo
subsystem 就是这种方法的一个例子。
调用软件使用一个库(通常用 C 编写以利用 C 编译器可以为它做的事情),该库能够加载 C 动态库并正确调用它们的函数来执行所有必需的数据设置。请参阅this 了解更多信息。
Python 的 ctypes
模块正是实现了这种方法。
我很难将 Java 的 JNI
和 .NET 的 P/Invoke
分为这两类。可能他们坐在中间的某个地方。
【讨论】:
是的。我相信像打开/关闭这样的 API 的实现会设置一些东西(要共享的参数、返回堆栈指针等)并有一些汇编代码来发送 s/w 中断(例如陷阱)到 cpu。对于 C 程序,这些库将在运行时加载和调用,就像任何 .so 一样。那么,如果采用第一条路由(libc),如何从python/java调用libc函数?) @tan,更新了我的答案以解决您评论中的问题。以上是关于非 c 语言如何与操作系统交互?的主要内容,如果未能解决你的问题,请参考以下文章