我可能很晚才进入这个问题,但是,它可能对其他一些程序员有用。首先 - 理论。
现代操作系统将虚拟化内存,为此,它在其系统内存区域内维护一系列页面指针。每个页面都有固定的大小(通常为 4K),当任何程序寻找一些内存时,它分配的内存地址是使用内存页面指针虚拟化的。它近似于前一代处理器中“段”寄存器的行为。
现在,当调度程序决定让另一个进程运行时,它可能会将前一个进程保留在内存中,也可能不会。如果它保存在内存中,那么调度程序所做的就是保存整个寄存器快照(现在,包括 YMM 寄存器 - 这个位之前是一个复杂的问题,因为没有单个指令可以保存整个上下文:在 XSAVE 上阅读),并且它具有固定格式(可在英特尔软件手册中找到)。这与正在使用的内存页面上的信息一起存储在调度程序本身的内存空间中。
但是,如果调度程序需要将即将进入睡眠状态的当前进程上下文“转储”到硬盘上——这种情况通常发生在正在唤醒的进程需要大量内存时,那么调度程序会写入内存页面磁盘块中的文件(称为页面文件 - 内存的保留区域 - 也是“老祖母智慧”的来源,即页面文件必须等于实际内存的大小)并且调度程序将内存页面指针地址保留为页面文件中的偏移量。当它醒来时,调度程序从页面文件中读取偏移地址,分配实际内存并填充内存页面指针,然后从磁盘块中加载内容。
现在,回答您的具体问题: 1. 您只需要使用相对寻址,还是可以使用绝对寻址?
和。您可以使用其中任何一种——无论您认为是绝对的还是相对的,因为内存页面指针以不可见的格式将该地址相对化。除了操作系统本身的内核之外,任何地方(包括 io 设备内存)都没有真正的绝对内存地址。为了测试这一点,您可以反汇编任何 .EXE 程序,以查看入口点始终是 CALL 0010,这清楚地表明每个线程获取不同的“0010”来开始执行。
- 线程如何获得生命,如果它放弃未使用的切片会怎样。
答。线程通常会得到一个切片——现代系统通常有 20ms 的标准——但这有时会在特殊用途的编译中改变,用于没有很多硬件中断要处理的服务器——按照它们在进程队列中的位置顺序。线程通常通过调用函数 sleep() 来放弃其切片,这是一种正式的(也是非常好的方式)放弃时间切片的余额部分。大多数实现异步读取或中断操作的库在内部调用 sleep(),但在许多情况下,顶级程序也调用 sleep() - 例如创建时间间隔。调用 sleep 肯定会改变进程上下文 - 实际上 CPU 没有使用 NOP 来自由休眠。
另一种方法是等待 IO 完成,处理方式不同。请求 IO 进程的程序将放弃其时间片,并且进程调度程序将此线程标记为处于“等待 IO”状态 - 并且在其预期的 IO 之前,处理器不会为该线程分配时间片已完成或超时。此功能有助于程序员,因为他们不必显式编写 sleep_until_IO() 类型的接口。
相信这会让你在探索中走得更远。