了解操作系统中的虚拟内存:概念和实现
运行内存分配程序的多个实例时出现意外行为。
我正在学习操作系统并遵循 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
最佳答案
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
-
已经完成的作业必须手动清理(删除),可以通过或者如上所示,通过传递
-AutoRemoveJob
给Receive-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:
来引用调用者的变量无疑是方便的,但它也造成了歧义,可能使特定于作业的变量无法访问;有关讨论,请参阅
|
"Virtualizing the CPU"
– 仅使用虚拟内存,这是完全不同的东西–
&
中的第二个./mem &; ./mem
,因此第二个进程在前台mem
运行 并直接将其输出打印到终端。第一个进程的输出是描述后台操作员创建的 PowerShell 后台作业的对象。&
–
|