我正在使用Debian压缩,并注意到内存始终为零。这是Linux发行版中的新功能吗?不久前,我相信我可以使用puts()并输出垃圾信息。
我多次运行此测试程序,但注释结果始终相同。 (我在sysctl.conf中有randomize_va_space = 2,所以我知道每次运行都使用不同位置的内存。)
char *a = malloc(50000000);
a[49999999] = '\0';
puts(a); // it outputs nothing since all are zeroes
printf("%p\n", a);
if(a[5000] == '\0') // this condition is always true
{
puts("It is a nul char.");
}
是否可以使系统的内存不为零?这个Debian squeeze安装可以激活哪些选项以使内存始终为零?
[在任何现代操作系统上,新获得的内存将包含非零值的唯一方法是malloc
是否重用了以前由程序释放的内存。从操作系统(内核)获得新的内存时,它最初是purely virtual。它没有物理上的存在。而是将其映射为一个完整的0字节共享存储页面的写时复制映射。首次尝试写入时,内核将捕获写入,分配新的物理内存页,将原始页的内容(在本例中均为0字节)复制到新页,然后恢复您的程序。如果内核知道新分配的物理内存已经为零,那么它甚至可以优化复制步骤。
此过程既必要又有效。这是必要的,因为将可能包含内核或其他用户进程的私有数据的内存移交给您的进程将是严重的安全漏洞。这是有效的,因为在分配时不执行调零。 “零填充”页面仅引用共享的零页面。
根据我在Linux Kernel Development中阅读的内容,内核执行零页操作,因为它可能包含用户程序可以解释并以某种方式获得对系统访问权限的内核数据。
malloc向内核请求更多页面,因此内核负责您所接收的内存。
第一次分配大块内存时,它很有可能会为零,因为系统调用(sbrk,mmap)分配的内存已由内核清零。但是,如果您释放并再次分配malloc,则内存将被回收,并且可能不包含零。
您的代码不会测试所有内存是否都为零-它会测试两个特定字节是否为零-a [0]和a [5000]。另外,malloc()与内核无关-它是C库函数,而不是系统调用。它的实现者几乎不可能将内存归零-您所看到的只是您的特定配置的一些随机怪癖。
[您会发现在进程之间具有隔离的大多数操作系统中,内存为零。原因是不允许某个进程窥视另一个进程释放的内存,因此必须在某个进程释放它的时间与另一个进程释放它的时间之间擦除一个内存页。实际上,擦除意味着清零,并且通常在进程分配内存时将其清零。
当您在玩具程序中调用malloc
时,该存储器尚未被用于其他任何用途。因此它仍然是内核中的零填充。如果尝试使用已经分配并释放了很多堆块的真实程序,您会发现已经使用过的内存由您的进程仍然包含您(或内存管理系统)可能放入的任何垃圾那里。
如前所述,关键区别是第一次分配 vs. 分配。如果您尝试:
char *a, tst;
do {
a = malloc(50000000);
a[49999999] = '\0';
printf("%50s\n%p", a, a); // it outputs nothing 1st, but bbbb.... 2nd
tst = a[5000]
memset(a, 'b', 50000000);
free(a);
} while (tst == '\0');
它将打印出两行(最有可能的是,至少在指针相同的情况下)。
关键是malloc()
返回的存储块具有未定义的内容。它可以为零,也可以不为零,并取决于程序过去如何进行内存分配(或使用了哪种内存调试工具)。
如果要保证内容,则在分配后需要calloc()
或显式初始化。
另一方面,系统的完整性/数据分离保证意味着,系统要求的任何[[initial地址空间-通过sbrk()
或mmap(MAP_ANON)
-都必须初始化为零,因为此类内容的任何其他内容将包括安全漏洞。
malloc时,由[[malloc分配的内存设置为0。但是在释放第一个分配的内存后,重新使用malloc分配新的内存,新的内存与第一个内存具有相同的虚拟地址和相同的内容。
我的问题:内核如何知道内存是首先分配给进程的,并且需要将其设置为零?并且内核如何知道将内存重新分配给同一进程,并且不需要清除?谢谢!