2

归根结底,我们编写的每一段代码最终都会变成汇编程序,然后变成机器语言。

如果您正在编写汇编程序并希望在两台计算机之间执行简单的连接,您如何知道在汇编程序中使用哪些内存地址(更不用说偏移量)?您是否需要知道与操作系统相关的特定地址?

我只是想知道有人会如何编写一个真正“干净”和“高效”的消息传递库/编译器——让我明白的是,网络通信/IPC 在汇编器中到底会是什么样子?

我认为这个答案的一部分可能在于查询与操作系统相关的已知地址?例如 0x4545456 到 0x 60000000 包含用于通信 X 等的 Linux 内核数据。

4

2 回答 2

1

有两段代码用于在汇编中进行网络连接 - 操作系统使用内核代码实际进行网络连接,以及想要告诉操作系统通过网络发送哪些数据的客户端代码。

通常,机器中的硬件具有特定的内存地址,专用于与网络硬件进行通信。然后,操作系统的机器代码可以将适当的值写入此内存,以控制最终发送和接收字节的硬件。这些内存地址将被硬编码到机器代码中。

对于进行网络连接的用户代码(例如,Mozilla Firefox),该过程是不同的。通常有一条机器指令或一组指令用于用户代码告诉操作系统执行某些任务(例如,在 MIPS 中,这是syscall,而我认为 x86 使用int操作说明)。客户端代码将通过使用适当的数据设置一些缓冲区来发送到网络,然后使用上面的汇编指令之一告诉操作系统它应该发送数据。硬件然后调用操作系统,它读取用户数据,然后使用自己的机器代码(如上所述)来适当地实际控制网络设备。通过这种方式,操作系统可以通过阻止对控制设备的物理地址的访问以及通过系统调用调节访问来保护对网络设备的直接访问。这也意味着您在编写用户代码进行联网时不需要知道任何内存地址。操作系统处理这些细节,您只需要知道执行什么指令来触发系统调用。

希望这可以帮助!

于 2012-04-08T19:34:21.587 回答
1

这些地址并不特定于您的操作系统。它们特定于您的硬件/系统。访问这些与汇编程序与另一种编程语言(例如 C)无关,实际上大多数设备驱动程序代码(实际与网络硬件交互的代码)通常是用 C 编写的。

这只是网络(以太网)控制器的一个随机样本:

英特尔® 82580EB/82580DB GbE 控制器:数据表

您的软件(无论是汇编程序还是其他语言)都必须对大量寄存器进行编程,以使该事物能够通过以太网进行实际通信。从一个更简单的例子开始可能更容易,比如串行端口。让我们构建一个假设的、固定波特率的串行端口控制器,映射到内存:

Address  Meaning
0        RX status (reads 0 when no data to read, 1 a byte is available)
1        RX buffer
2        TX status (reads 0 when ready to send, 1 when busy)
3        TX buffer

现在您的软件,无论是汇编程序还是任何其他语言,都可以通过监视(轮询)地址 2 直到它准备好,将下一个字节写入地址 3 来将数据传输到另一台计算机。我们还可以通过监视(轮询)从另一台计算机接收数据) 地址 0 以查看数据何时准备好,并在数据存在时从地址 1 读取字节。

在现代操作系统/操作系统中,这些都是物理地址,需要以某种方式映射到虚拟地址。

现实世界的硬件,比如我链接的那个,通常会使用中断,所以你不需要轮询。它通常具有 DMA,因此硬件可以直接访问您的数据,而不是您逐字节地输入数据。它将处理各种协议,并将具有用于检查和设置该协议各个方面的寄存器。

在现代操作系统中,与硬件的实际交互是在设备驱动程序中实现的,用户软件可以通过一些 API 与设备驱动程序交换数据。同样,此用户代码可以用汇编程序或任何其他语言编写。API 会因操作系统而异。通信/网络通常构建为“堆栈”,在较低级别的协议上实现较高级别的协议。该堆栈的哪一部分在用户库中或操作系统的一部分将因不同的操作系统而异。

对于我上面描述的假设设备,API 可能包含两个单字节阻塞调用,read()并且write(). 然后,您使用来自汇编程序或更高级别语言的某种系统调用机制来调用这些并传递参数/检索输出。在某些操作系统中,设备 I/O 可能看起来像文件 I/O,因此您将使用通用文件读/写来在设备上执行操作,并且操作系统会将这些操作分派给正确的设备驱动程序。此外,在典型的操作系统中,实际的系统调用将通过某种库提供,您可以再次从各种编程语言中调用这些库。

于 2012-04-08T20:00:22.620 回答