0

我了解机器代码中的程序如何将内存中的值加载到寄存器中、执行跳转或将寄存器中的值存储到内存中,但我不明白这对多个进程是如何工作的。一个进程是动态分配内存的,所以它必须使用相对寻址吗?这是自动完成的(意味着有执行相对跳转的汇编指令等),还是程序必须“手动”将正确的偏移量添加到它所寻址的每个内存位置。

我还有一个关于多任务处理的问题,这有点相关。未运行的操作系统如何停止线程并继续下一个线程。这是通过定时中断完成的吗?如果是这样,那么如何为线程保留寄存器中的值。在将控制权交给不同的线程之前,它们是否已保存到内存中?或者,不是定时中断,线程只是简单地选择一个放弃控制的好时机。在定时中断的情况下,如果线程被赋予处理器时间而它不需要它会发生什么。它是否必须浪费它,它可以手动调用中断,还是它会提醒操作系统它不需要太多时间?

编辑:或者在运行之前编辑可执行文件以补偿正确的偏移量?

4

3 回答 3

2

这不是它的工作原理。所有现代操作系统都会虚拟化可用内存。给每个进程一个幻觉,即它拥有 2 GB 内存(或更多)并且不必与任何人共享它。执行此操作的机器中的关键组件是MMU,现在内置在处理器本身中。这种虚拟化的另一个核心特性是它隔离了进程。一个行为不端的人不能让另一个人失望。

是的,时钟滴答中断用于中断当前运行的代码。处理器状态只是保存在堆栈上。然后,操作系统调度程序检查是否有任何其他线程准备好运行并且具有足够高的优先级来排在第一位。一些额外的代码确保每个人都能得到公平的份额。然后只需将 MMU 设置为在另一个线程上恢复执行即可。如果没有线程准备好运行,则 CPU 将使用 HALT 指令物理关闭。被下一个时钟中断再次唤醒。

这是一万英尺的视图,任何关于操作系统设计的书都很好地涵盖了它。

于 2012-04-17T05:49:01.757 回答
1

一个进程是动态分配内存的,所以它必须使用相对寻址吗?

不,它可以使用相对或绝对寻址,具体取决于它试图解决的问题。

至少从历史上看,各种不同的寻址模式更多地是关于本地内存而不是远程内存。相对寻址适用于接近当前地址的内存地址,而绝对寻址更昂贵,但可以解决任何问题。对于现代虚拟内存系统,这些区别可能不再需要。

一个进程是动态分配内存的,所以它必须使用相对寻址吗?这是自动完成的(意味着有执行相对跳转的汇编指令等),还是程序必须“手动”将正确的偏移量添加到它所寻址的每个内存位置。

我不确定这个。这通常由编译器处理。同样,现代虚拟内存系统使这种复杂性变得不必要。

在将控制权交给不同的线程之前,它们是否已保存到内存中?

是的。通常,所有状态(寄存器等)都存储在过程控制块 (PCB) 中,加载新上下文,从新 PCB 加载寄存器和其他上下文,并在新上下文中开始执行。PCB 可以存储在堆栈或内核内存中,或者可以利用处理器特定的操作来优化此过程。

或者,不是定时中断,线程只是简单地选择一个放弃控制的好时机。

线程可以让出控制权——将自己放回运行队列的末尾。它也可以等待一些 IO 或休眠。然后线程库将线程放入等待队列并切换到另一个上下文。当 IO 准备好或 sleep 到期时,线程被放回运行队列。互斥锁也是如此。它在等待队列中等待锁。一旦锁可用,线程将被放回运行队列。

在定时中断的情况下,如果线程被赋予处理器时间而它不需要它会发生什么。它是否必须浪费它,它可以手动调用中断,还是它会提醒操作系统它不需要太多时间?

线程要么可以运行(执行 CPU 指令),要么正在等待——在 IO 或睡眠中。它可以要求让步,但通常它是通过 [再次] 休眠或等待 IO 来实现的。

于 2012-04-17T05:04:49.617 回答
0

我可能很晚才进入这个问题,但是,它可能对其他一些程序员有用。首先 - 理论。

现代操作系统将虚拟化内存,为此,它在其系统内存区域内维护一系列页面指针。每个页面都有固定的大小(通常为 4K),当任何程序寻找一些内存时,它分配的内存地址是使用内存页面指针虚拟化的。它近似于前一代处理器中“段”寄存器的行为。

现在,当调度程序决定让另一个进程运行时,它可能会将前一个进程保留在内存中,也可能不会。如果它保存在内存中,那么调度程序所做的就是保存整个寄存器快照(现在,包括 YMM 寄存器 - 这个位之前是一个复杂的问题,因为没有单个指令可以保存整个上下文:在 XSAVE 上阅读),并且它具有固定格式(可在英特尔软件手册中找到)。这与正在使用的内存页面上的信息一起存储在调度程序本身的内存空间中。

但是,如果调度程序需要将即将进入睡眠状态的当前进程上下文“转储”到硬盘上——这种情况通常发生在正在唤醒的进程需要大量内存时,那么调度程序会写入内存页面磁盘块中的文件(称为页面文件 - 内存的保留区域 - 也是“老祖母智慧”的来源,即页面文件必须等于实际内存的大小)并且调度程序将内存页面指针地址保留为页面文件中的偏移量。当它醒来时,调度程序从页面文件中读取偏移地址,分配实际内存并填充内存页面指针,然后从磁盘块中加载内容。

现在,回答您的具体问题: 1. 您只需要使用相对寻址,还是可以使用绝对寻址?

和。您可以使用其中任何一种——无论您认为是绝对的还是相对的,因为内存页面指针以不可见的格式将该地址相对化。除了操作系统本身的内核之外,任何地方(包括 io 设备内存)都没有真正的绝对内存地址。为了测试这一点,您可以反汇编任何 .EXE 程序,以查看入口点始终是 CALL 0010,这清楚地表明每个线程获取不同的“0010”来开始执行。

  1. 线程如何获得生命,如果它放弃未使用的切片会怎样。

答。线程通常会得到一个切片——现代系统通常有 20ms 的标准——但这有时会在特殊用途的编译中改变,用于没有很多硬件中断要处理的服务器——按照它们在进程队列中的位置顺序。线程通常通过调用函数 sleep() 来放弃其切片,这是一种正式的(也是非常好的方式)放弃时间切片的余额部分。大多数实现异步读取或中断操作的库在内部调用 sleep(),但在许多情况下,顶级程序也调用 sleep() - 例如创建时间间隔。调用 sleep 肯定会改变进程上下文 - 实际上 CPU 没有使用 NOP 来自由休眠。

另一种方法是等待 IO 完成,处理方式不同。请求 IO 进程的程序将放弃其时间片,并且进程调度程序将此线程标记为处于“等待 IO”状态 - 并且在其预期的 IO 之前,处理器不会为该线程分配时间片已完成或超时。此功能有助于程序员,因为他们不必显式编写 sleep_until_IO() 类型的接口。

相信这会让你在探索中走得更远。

于 2015-03-27T15:58:16.573 回答