关于如何在原始汇编中实现多线程的问题:现代 32 位 x86 处理器具有对任务切换的硬件支持。您可以使用它来实现多任务(具有不同地址空间的多个进程)和/或多线程(单个进程中的多个执行线程)。
此功能记录在Intel 64 and IA-32 Architectures Software Developer's Manual,第 3 卷,第 7 章,任务管理中。 注意:阅读和理解这份 4700 页的文档,可能需要 12 个生命周期。
使用这种硬件支持是运行某些东西最直接的方法,但可能不是最有效的方法。早期基于 x86 的操作系统从这种模式转向手动任务切换。这允许系统实现 CPU 不提供的切换功能,并且还可以在完全切换过度时执行优化切换。这种方法变得如此普遍,以至于 x86 处理器的 64 位长模式放弃了对硬件任务管理的支持。现代操作系统使用手动任务切换。
重新将移动指令作为实现多线程的一种方式的想法,我认为您的概念是在需要切换到新代码时在地址空间中移动代码。有 2 种替代技术可以消除这样做的需要。
首先,您可以跳转到不同位置的代码,而不是移动代码。早期的多任务操作系统(如 PDP-11 上的 Unix)使用了这种技术。您可以将所有活动程序加载到不同位置的内存中,设置周期性中断以每隔一段时间将控制权交给您的系统软件,并在每次中断时选择要跳转到的下一个程序。系统应跟踪每个程序停止的位置,以便它可以在同一位置恢复。
第二种方法依赖于虚拟内存。仍然有一个物理内存空间。最重要的是,定义了多个“虚拟”内存空间。在任何给定时间,只有 1 个虚拟内存空间处于活动状态。每当访问内存时,系统都会将虚拟地址转换为某个物理地址。访问内存的程序只能看到自己的虚拟地址空间。
每个任务都有自己的地址空间。当切换到一个新任务时,系统会激活它的私有地址空间,然后在它上次暂停在私有地址空间的任何地方恢复执行。您实际上不是在单个共享地址空间中移动东西,甚至不是在共享地址空间中从一个地方跳到另一个地方,而是实际上定义了一整套独立的地址空间。您可以根据需要转移到新的空间。
现代多线程在很大程度上依赖于这种方法。每个进程都会有一个私有地址空间;系统根据需要在它们之间切换。一个进程将有 1+ 个线程。进程中的每个线程共享相同的地址空间。然后,每个线程存储的所有内容都是上次暂停执行的位置。在同一进程中切换到新线程会保留当前地址空间,并在其中跳转到新线程上次暂停的位置。
关于如何移动指令的问题,汇编语言不提供这种能力。这不是一件常见的事情。如果您想对内存中的机器代码进行操作,您可能需要使用像 Intel X86 Encoder Decoder library 这样的库。像这样的库了解存在的指令和机器代码的字节格式。这使它能够将字节序列解释为指令。
一旦您知道以下指令有多大,移动该字节数在汇编中就很容易了。在 x86 中,您将使用 mov 指令。