了解操作系统中的虚拟内存:概念和实现

运行内存分配程序的多个实例时出现意外行为。

我正在学习操作系统并遵循 OSTEP(操作系统:三个简单部分)教科书。我试图通过同时运行程序的多个实例来理解内存虚拟化。这是我正在使用的代码:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"

int main(int argc, char *argv[]) {
    int *p = malloc(sizeof(int)); // a1
    assert(p != NULL);
    printf("(%d) address pointed to by p: %p\n", getpid(), p); // a2
    *p = 0; // a3
    while (1) {
        Spin(1);
        *p = *p + 1;
        printf("(%d) p: %d\n", getpid(), *p); // a4
    }
    return 0;
}

根据教科书,当使用命令运行时./mem &; ./mem &,该程序应该通过同时运行两个实例来演示内存虚拟化。文本指出:

我们从示例中看到,每个正在运行的程序都在同一地址 (0x200000) 分配了内存,但每个程序似乎都在独立更新 0x200000 处的值!就好像每个正在运行的程序都有自己的私有内存,而不是与其他正在运行的程序共享相同的物理内存。事实上,这正是操作系统虚拟化内存时发生的情况。

但是,当我在本地运行该程序时,我得到以下输出:

PS C:\Users\malan\Desktop\os\Virtualizing_the_Cpu> ./mem &; ./mem

Id   Name             PSJobTypeName   State
--   ----             -------------   -----
13   Job13            BackgroundJob   Running
(8776) address pointed to by p: 007B1990
(8776) p:1
(8776) p:2
(8776) p:3
(8776) p:4
(8776) p:5
(8776) p:6
(8776) p:7
(8776) p:8
(8776) p:9
(8776) p:10
(8776) p:11
(8776) p:12
(8776) p:13

两个实例没有按预期同时运行。是什么原因导致了这种现象?我的设置中是否遗漏了某些内容,或者我对其工作原理的理解是否不正确?

附加信息:

  • 操作系统:Microsoft Windows 11 家庭单语言版
  • 操作系统版本:10.0.22631 N/A 内部版本 22631
  • 编译器:gcc(MinGW.org GCC-6.3.0-1)6.3.0

2

  • 1
    事实并非如此"Virtualizing the CPU"– 仅使用虚拟内存,这是完全不同的东西


    – 

  • 另外:由于您忘记了&中的第二个./mem &; ./mem,因此第二个进程在前台mem运行 并直接将其输出打印到终端。第一个进程的输出是描述后台操作员创建的 PowerShell 后台作业的对象。&


    – 


最佳答案
3

将其输入到 bash 中它就会起作用。

& 在 bash 中表示在后台运行,但在 powershell 中则表示其他意思;我很惊讶你没有收到错误消息。

1

  • – 与内置的Windows PowerShell版本不同 –支持在 Windows 上启动后台作业的后。虽然这些作业的工作方式和管理方式与 Bash 不同,但可以轻松调整命令行以获得所需的行为。


    – 

命令末尾&的 是 bash 指令(即 Linux / Unix 命令行),用于在后台运行程序。Windows 下没有此功能。

最简单的做法是打开两个单独的命令提示符,并大致同时在每个命令提示符中运行该程序。

2

  • – 与内置的Windows PowerShell版本不同 –支持在 Windows 上启动后台作业的后置操作&,如问题中显示的输出所示。但是,其输出行为与 Bash 版本不同。


    – 

  • 也就是说,很容易调整命令行以获得所需的行为,甚至可以通过使用后台运算符()使其在Windows PowerShell中工作。Start-Job&


    – 

总结

(您正在运行)中,使用以下命令:

(./mem &), (./mem &) | Receive-Job -Wait -AutoRemoveJob

Windows PowerShell(旧版,随 Windows 一起提供,其最新版本5.1)中:

(Start-Job { ./mem }), (Start-Job { ./mem }) | Receive-Job -Wait -AutoRemoveJob

Bash 和 PowerShell 作业之间的区别:
  • 实际上,命令./mem &; ./mem & 行用于bash(或另一个与 POSIX 兼容的 shell,例如zsh):

    • 它创建两个在后台运行的,并且(除了作业控制消息)将进程的输出直接异步发送到终端
  • 相同的命令行在PowerShell (Core) 7中在语法上也可以工作(尽管在仅支持的Windows PowerShell不行,见下文),但在那里功能不同Start-Job

    • &,后置,是调用(./mem &的缩写Start-Job { ./mem }[1]的语法糖,会创建一个 PowerShell 后台,这些调用返回的是一个作业对象,可以使用诸如的 cmdlet 进行查询和管理

    • 虽然这些作业与 Bash 作业大致类似,但仍存在重要区别:

      • PowerShell 作业不会自动将其输出打印到调用者的终端;相反,输出会在后台悄悄收集,并可根据需要使用Receive-Object

      • 已经完成的作业必须手动清理(删除),可以通过或者如上所示,通过传递-AutoRemoveJobReceive-Job(这也需要-Wait同步等待作业完成,以便可以收集其所有输出)。


[1] 但是有一个重要的区别:使用 时Start-Job,必须使用脚本块内的{ ... }( ) 才能从调用者的作用域中引用变量值,而使用 &所有变量引用都隐式引用调用者的变量。例如,给定一个调用者变量,$foo = 'bar'相当于Start-Job { "Caller's `$foo value: $using:foo" }"Caller's `$foo value: $foo" &但是,是 – 明智地 – 特殊情况,并引用作业的进程 ID(您可以使用$using:PID来引用调用者的进程 ID)。虽然在使用后台运算符时不必使用$using:来引用调用者的变量无疑是方便的,但它也造成了歧义,可能使特定于作业的变量无法访问;有关讨论,请参阅