C运行时和库是Unix API的合法部分吗?

2020-09-27 00:57:57

引用调试evilGo运行时错误(部分是通过)的话来说,对Go的打击之一是:

Go碰巧有一个重新发明自己的标准库的策略(在我看来相当疯狂),所以它不使用任何标准的Linux glibc代码来调用vDSO,而是使用自己的调用(以及syscall)。

Unix上的普通非C语言通常通过调用标准C库来实现大量低级操作。这从进行系统调用之类的事情开始,但也包括像getaddrinfo(3)这样的操作。Go并不这样做;它自己尽可能多地实现,直接使用汇编语言直接进行系统调用。偶尔也会有随之而来的问题。

一些UNIX明确表示标准C库是稳定的API和系统接口点;Solaris(现在是Illumos)就是一个例子。虽然他们不会随意更改底层系统调用实现,但据我所知,Illumos官方保留更改所有实际系统调用的权利,从而打破任何没有动态链接到libc的用户空间代码。如果您的代码崩溃,那是您的错;我告诉您,到libc的动态链接是官方API。

其他工会只是默契地、通过积累来做到这一点。例如,在任何使用nsswitch.conf的Unix上,对于通过标准C库传出的getaddrinfo()这样的操作,总是很难获得相同的结果,因为这些操作可能使用通过libc访问的任意和奇怪的动态加载模块,并且需要各种随机的libc API才能工作。这指出了这里的一个问题:一旦您开始(间接)调用libc API的随机位,它们可能会非常合理地对它们正在运行的运行时环境做出假设。如何设置有限的标准C库运行时环境通常没有文档记录;相反,官方视图通常是让标准C库运行时代码启动main()函数。

我一点也不确定所有这些要求和与标准C库及其隐式运行时环境的纠缠是否都是好事。标准C库的运行时环境是为C设计的,它通常包含一系列关于事物如何工作的错综复杂的假设。强迫所有其他语言适应这些未记录的约束显然是受限的,而且标准C库通常不是设计成透明的API;事实上,至少GNU libc故意操纵它在幕后所做的事情,以便对C程序更有用。这些操作对于您的非C语言是否有用或是否受欢迎是一个悬而未决的问题,但是GNU libc人员甚至不一定要记录它们。

(Marcan的故事表明,对于任何试图在进入libc时使用最少堆栈的语言环境来说,标准C库行为都会是一个问题,这里是以内核vDSO的形式设计为通过libc进行调用的。?这也显示了问题的另一个方面,据我所知,调用标准C库时必须提供多少堆栈空间通常没有文档记录。它只是假设你会有足够的钱,不管是什么。(C代码会;那些试图运行自己的协程和线程环境的人,可能不会。)。

这种隐含的假设在Unix中由来已久。许多UNIX实际上只以标准C库接口的形式记录了它们的系统调用,悄悄地回避了用户空间的内核API和标准C库apito C程序之间的区别。如果你幸运的话,你可以找到一些关于如何进行原始系统调用的文档,以及这些原始系统调用在不寻常的情况下会返回什么东西,比如PIPE(2)。我认为没有多少UNIX试图将内核API与标准的C库API分开显式而完整地记录下来,特别是当你遇到像ioctl()这样的情况时(通常有C宏和#定义用来形成一些参数,当然它们只记录在C头文件中)。