为什么我可以访问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 有固定大小吗?还能有什么对象?
这里有两种不同的内存分配器:
- 操作系统,可在 Unix 下访问,例如
brk(2)
or mmap(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" 中,这是一大块内存。由于所有对象都分配在那里,因此内存区域非常大并且充满了任何类型的对象。写入此内存可能会使您的程序崩溃。
在 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 有固定大小吗?还能有什么对象?
这里有两种不同的内存分配器:
- 操作系统,可在 Unix 下访问,例如
brk(2)
ormmap(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" 中,这是一大块内存。由于所有对象都分配在那里,因此内存区域非常大并且充满了任何类型的对象。写入此内存可能会使您的程序崩溃。