在现代软件开发和编程时代,术语“运行时”可能具有不同的含义,具体取决于上下文和所讨论的语言。我在这里澄清这些差异,重点关注与 Java 或 Python 等更现代的语言相比,C 语言的运行时如何工作。我打算让这篇文章重点关注初学者程序员,因此我将避免深入研究复杂的概念。
从本质上讲,运行时是一个程序本身,它读取并执行开发人员编写的代码。但当一些开发人员使用 C 语言运行时时,就会变得混乱。
在 Java 或 Python 等语言中,运行时本身就是一个读取 myfile.js 文件的程序,这就是为什么您运行 Nodejs 程序,例如:node myfile.js 和 v8 引擎(是 JavaScript 引擎,它解析并执行 JavaScript代码。)管理它的一切,无论你创建一个新文件,启动一个子进程等等,最重要的是你不能做任何 v8 不允许你做的事情。
但是当你运行一个c程序时,你不需要执行c myfile.c,你只需要编译一次,现在你不再需要gcc了,直接运行它即可。
在 C 中,没有像 Java 或 Python 那样与代码一起运行的单独程序。相反,通常所说的C“运行时”实际上是在编译期间添加的一组静态插入的代码和指令。它是最终二进制文件中包含的最小指令集,用于处理 CPU/操作系统级别的某些必要任务。它处理函数调用的堆栈帧创建和拆卸(在汇编中使用 PUSH、POP、CALL、RET 等指令)。即使这一点也可以通过使用内联汇编提供您自己的 __start 函数来覆盖,从而使开发人员能够完全控制程序的入口点和初始化。
void __start() { // Custom entry point, no standard library initialization // You have no access to argc and argv here unless you access them manually from registers // you can create you own custom stack setup, initialization and etc here. // Exit directly using a syscall asm("mov $60, %rax; mov $0, %rdi; syscall"); // exit(0) syscall }
这看起来根本不像运行时,它只是编译器添加的一些汇编语言代码,因此开发人员不必这样做。
在 C 语言中,您可以使用内联汇编直接调用系统调用,以操作系统通常不允许的方式与内核交互,这就是恶意软件的创建方式。内联汇编允许开发人员在 C 代码中编写汇编语言指令。这通常用于性能关键的代码或访问特定的硬件功能。
在linux C中有一个FLAG,允许您直接将文件数据写入存储设备,绕过一些内核的缓存机制,称为O_DIRECT标志,它与open和write系统调用结合使用。该标志确保数据不会在 RAM 中缓冲或由内核在内核空间中管理,这会直接将数据写入硬盘,JVM 不允许您这样做,这只是一个简单的示例。
这是一个简单的例子:
asm volatile ( "syscall" : "=a" (written) : "0" (1), "D" (fd), "S" (buffer), "d" (BLOCK_SIZE) : "rcx", "r11", "memory" );
*注意:*(写入)是在 main() 内部创建的变量,(1) 是写入的系统调用号,(fd) 是文件将被写入的位置,即 int fs = open("path .log",O_WRONLY; (BLOCK_SIZE) 是另一个变量名。它比那个更复杂。
了解运行时概念多年来的演变非常重要。 70 年代的 C“运行时”与我们在 2000 年代的语言中看到的健壮的运行时环境有很大不同。在讨论运行时时,这种演变可能会导致混乱,尤其是在熟悉不同编程时代的开发人员之间。
我认为人们现在正在将 1970 年代的运行时与 2000 年代的运行时进行比较,这让新开发人员与老开发人员感到困惑。
解决特定问题是任何编程语言的主要任务,你不想用 C 编写一个完整的框架来创建 API,我们有 NodeJS,它很擅长,你不需要用 JavaScript 编写裸机代码,因为我们已经有了 C 并且它在这方面非常棒。为什么要重新发明轮子,让我们用轮子创造一辆神奇的汽车,除非你不想在火星上驾驶它。
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3