我理解机器代码中的程序如何将值从内存加载到寄存器,执行跳转或将寄存器中的值存储到内存中,但我不明白它如何适用于多个进程。进程是动态分配内存,所以它必须使用相对寻址吗?这是自动完成的(意味着有执行相对跳转的汇编指令等),或者程序是否必须“手动”将正确的偏移量添加到它所处理的每个存储器位置。
我还有一个与多任务相关的问题。未运行的操作系统如何停止线程并继续执行下一个操作。这是用定时中断完成的吗?如果是这样,那么如何为线程保留寄存器中的值。在将控制权交给另一个线程之前,它们是否已保存到内存中?或者,而不是定时中断,线程只是选择放弃控制的好时机。在定时中断的情况下,如果线程被给予处理器时间并且它不需要它会发生什么。是否必须浪费它,是否可以手动调用中断,还是警告操作系统它不需要太多时间?
编辑:或者是在运行之前编辑的可执行文件以补偿正确的偏移量?
答案 0 :(得分:2)
这不是它的工作原理。所有现代操作系统都虚拟化可用内存。给每个进程一个错觉,它有2千兆字节的内存(或更多),而不必与任何人分享。执行此操作的计算机中的关键组件是the MMU,现在内置于处理器本身。此虚拟化的另一个核心功能是它隔离了进程。一个行为不端的人不能带来另一个人。
是的,时钟节拍中断用于中断当前运行的代码。处理器状态只是保存在堆栈中。然后,操作系统调度程序检查是否有任何其他线程准备好运行,并且具有足够高的优先级以获得第一个。一些额外的代码确保每个人都获得公平的份额。然后,只需将MMU设置为在另一个线程上恢复执行即可。如果没有准备好运行的线程,则使用HALT指令将CPU物理关闭。被下一个时钟中断再次唤醒。
这是一万英尺的视图,它在任何有关操作系统设计的书中都有详细介绍。
答案 1 :(得分:1)
进程是动态分配内存,所以它必须使用相对寻址吗?
不,它可以使用相对或绝对寻址,具体取决于它试图解决的问题。
至少在历史上,各种不同的寻址模式更多地是关于本地存储器和远程存储器。相对寻址是针对接近当前地址的内存地址,而绝对是更昂贵但可以解决任何问题。使用现代虚拟内存系统,可能不再需要这些区别。
进程是动态分配内存的,所以它必须使用相对寻址吗?这是自动完成的(意味着有执行相对跳转的汇编指令等),或者程序是否必须"手动"将正确的偏移量添加到它所处的每个内存位置。
我不确定这个。这通常由编译器负责。同样,现代虚拟内存系统使得这种复杂性变得不必要。
在将控制权交给另一个线程之前,它们是否已保存到内存中?
是。通常,所有状态(寄存器等)都存储在过程控制块(PCB)中,加载新的上下文,从新PCB加载寄存器和其他上下文,并在新的上下文中开始执行。 PCB可以存储在堆栈或内核存储器中,也可以利用特定于处理器的操作来优化此过程。
或者,而不是定时中断,线程只是选择放弃控制的好时机。
线程可以产生控制 - 将自己放回到运行队列的末尾。它也可以等待一些IO或睡眠。然后,线程库将线程放入等待队列并切换到另一个上下文。当IO准备就绪或睡眠过期时,线程将被放回运行队列中。互斥锁也会发生同样的情况。它等待等待队列中的锁定。一旦锁定可用,线程就会被放回到运行队列中。
在定时中断的情况下,如果线程被给予处理器时间并且它不需要它会发生什么。是否必须浪费它,是否可以手动调用中断,还是提醒操作系统它不需要太多时间?
线程可以运行(执行CPU指令)或正在等待 - 无论是IO还是休眠。它可以要求屈服,但通常它是通过[再次]睡觉或等待IO来实现的。
答案 2 :(得分:0)
我可能很晚才进入这个问题,但是,它可能对其他程序员有用。第一 - 理论。
现代操作系统将虚拟化内存,为此,它在其系统内存区域内维护一系列页面指针。每个页面都是固定大小(通常是4K),当任何程序寻找一些内存时,它分配的内存地址使用内存页面指针进行虚拟化。它近似于" segment"的行为。在上一代处理器中注册。
现在,当调度程序决定让另一个进程运行时,它可能会也可能不会将上一个进程保留在内存中。如果它将它保存在内存中,那么调度程序所做的就是保存整个寄存器快照(现在,包括YMM寄存器 - 之前这个位是一个复杂的问题,因为没有单个指令可以保存整个上下文:在XSAVE上读取),这有固定的格式(英特尔SW手册中提供)。它存储在调度程序本身的内存空间中,以及正在使用的内存页面上的信息。
但是,如果调度程序需要" dump"即将进入硬盘的当前进程上下文 - 这种情况通常在唤醒进程需要非常大量内存时出现,然后调度程序将内存页文件写入磁盘块(称为页面文件 - 保留内存区域 - 也是旧祖母智慧的来源"该页面文件必须等于实际内存的大小),并且调度程序将内存页面指针地址保留为页面文件中的偏移量。当它唤醒时,调度程序从页面文件中读取偏移地址,分配实内存并填充内存页指针,然后从磁盘块加载内容。
现在,回答您的具体问题: 1.您是否只需要使用相对寻址,或者您可以使用绝对值?
和。您可以使用任何一种 - 无论您认为是绝对的还是相对的,因为内存页指针以不可见的格式将该地址相对化。除了操作系统本身的内核之外,在任何地方都没有真正的绝对内存地址(包括io设备内存)。为了测试这个,你可以解组任何.EXE程序,看看入口点总是CALL 0010,这清楚地表明每个线程得到一个不同的" 0010"开始执行。
答。线程通常会得到一个切片 - 现代系统有20ms作为通常的标准 - 但是对于没有很多硬件中断需要处理的服务器的特殊用途编译有时会改变 - 按照它们在进程队列中的位置顺序。线程通常通过调用函数sleep()来放弃它的切片,这是一种正式(并且非常好的方式)放弃时间片的平衡部分。大多数实现异步读取或中断操作的库在内部调用sleep(),但在许多情况下,顶级程序也调用sleep() - 例如创造时间差距。对sleep进行调用肯定会改变进程上下文 - 实际上CPU不能自由地使用NOP进行休眠。
另一种方法是等待IO完成,并且处理方式不同。要求IO进程的程序将放弃其时间片,并且进程调度程序将该线程标记为"等待IO" state - 并且处理器不会给该线程一个时间片,直到其预期的IO完成或超时。此功能可以帮助程序员,因为他们不必显式编写sleep_until_IO()类型的接口。
相信这会让你在探索中更进一步。