OS X memset和系统跟踪

时间:2015-04-27 19:23:08

标签: c macos instruments memset

这是一个简化的程序:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

void *worker(void *data) {
    size_t size = 1000000;
    void *area = malloc(size);
    if (area != NULL) {
        memset(area, 0, size);
        sleep(1);
        free(area);
    }
    return NULL;
}

int main() {
    int number_of_threads = 4;
    pthread_t threads[number_of_threads];

    for (int i = 0; i < number_of_threads; i++) {
        if (pthread_create(&(threads[i]), NULL, worker, NULL)) {
            return 0;
        }
    }

    for (int i = 0; i < number_of_threads; i++) {
        pthread_join(threads[i], NULL);
    }

    return 0;
}

我使用命令iprofiler -systemtrace OSXMalloc获得以下系统跟踪:

enter image description here

为什么memset会产生所有这些零填充事件?他们是什么意思,为什么这么多?我知道我尝试用0填充1 MB但是为什么它不会在每个线程的单个调用中执行此操作?

3 个答案:

答案 0 :(得分:2)

这不是您在此处报告的memset,而是将页面映射到内存供以后使用的行为。操作系统用零填充页面,以防止数据从一个应用程序泄漏到另一个应用程序。

您看到的每个零填充事件都会为每个内存页面生成一次。单个内存页面只有4K长度 - 4096个字节 - 因此,您的连续块100万个字节跨越245个,可能还有246个单页。

对于所有内存页面,可能不需要此零填充事件。其中一些可能在空闲CPU时间内被清零(并且操作系统保留了“准备就绪”存储页面的列表),而其他页面可能被分配但从未被使用过。但是,在这种情况下,memset本身会尝试访问每个字节,因此操作系统别无选择,只能在memset到达之前清除页面。

答案 1 :(得分:1)

出于安全和隐私目的,内核需要保证新分配给进程的页面用零填充。否则,您可以从其他进程获取数据,包括密码或财务信息。

页面在首次访问时归零,类似于写时复制。由于memset()将遍历将它们归零的页面,因此内核将一次一个地填充页面。 memset()然后做了一堆冗余的工作,在已经归零的页面上写入零。

使用calloc()而不是malloc()后跟memset(..., 0, ...),您会得到更好的服务。由于malloc库知道内核将零填充新分配的页面,因此它知道它不需要执行显式memset()来满足calloc()的零填充契约。在第一次访问时仍会出现零填充错误,但是当内存真正首次使用时,它们将会发生。对于不需要的memset(),他们不会“急切地”完成。

顺便说一下,并非所有通过malloc()完成的分配都会从内核中获取新页面。有些人会重复使用先前在您的流程中分配和释放的页面。但是,对于您正在进行的大型分配,页面通常在malloc()期间分配,并在free()期间解除分配。

答案 2 :(得分:0)

将有4个线程,每个线程将调用malloc和memset,因此4个实例完全被占用。

但是,代码中存在错误。

  1. pthread_t数组预先设置为全零。

  2. 如果对pthread_create的任何调用失败,请将关联的pthread_t条目重新设置为0

  3. 如果调用pthread_create失败,请不要退出程序

  4. 在调用pthread_join的循环中, 如果关联的pthread_t条目为0 那么,不要为该条目打电话pthread_join

  5. 否则,当可能存在活动的pthread时程序正在退出。