为什么我可以访问Python中看似任意的内存地址?

Why can I access seemingly arbitrary memory addresses in Python?

在 NumPy 中使用 strides 我意识到你可以轻松地越过数组的边界:

>>> import numpy as np
>>> from numpy.lib.stride_tricks import as_strided
>>> a = np.array([1], dtype=np.int8)
>>> as_strided(a, shape=(2,), strides=(1,))
array([  1, -28], dtype=int8)

像这样我可以读取数组外的字节,也可以写入它们。但我不明白这怎么可能。为什么操作系统不阻止我?在抛出 Segmentation fault 之前,我似乎可以离开这个数组至少 100 KB。

我唯一能想到的就是这块内存space是我的Python进程直接分配的。 NumPy 会这样做吗?这个 space 有固定大小吗?还能有什么对象?

这里有两种不同的内存分配器:

  1. 操作系统,可在 Unix 下访问,例如brk(2) or mmap(2)。这通常会准确地满足您的要求,但对用户来说不是很友好。
  2. C 运行时堆,可通过 malloc(3)free(3) 访问。这可能会或可能不会 return 立即释放内存给操作系统。如果性能更高,它也可以将分配舍入到最近的页面。这通常根据 (1) 来实现。

大多数应用程序,包括 NumPy 和 Python,使用 (2) 而不是 (1)(或者它们在 (2) 之上实现自己的内存分配器)。结果,根据 (2) 无效的记忆可能根据 (1) 仍然有效。如果您违反方法 (1) 的规则,您只会遇到段错误。您也可能正在与堆上的其他活动对象进行交互,这很可能导致您的程序以任意方式出现异常行为,even if you are not changing anything

Python 并且 numpy 是用 C 构建的,它没有内置内存保护。内存分配在 "heap" 中,这是一大块内存。由于所有对象都分配在那里,因此内存区域非常大并且充满了任何类型的对象。写入此内存可能会使您的程序崩溃。