O processador não está vendo mudanças na memória compartilhada POSIX?

0

Contexto: Estou usando a memória compartilhada POSIX para fornecer um conjunto de processos com um espaço de memória compartilhada. Eu usei esse esquema já há algum tempo para compartilhar dados e está funcionando bem. No entanto, recentemente encontrei um problema estranho com uma determinada classe de programas.

Problema: Eu escrevi um programa no qual cada processo tem que contribuir com um valor para uma soma compartilhada no espaço de memória compartilhada. A soma foi inicializada para zero quando o objeto compartilhado foi mapeado na memória anteriormente. No entanto, quando cada processo tenta adicionar sua parte à soma compartilhada, ele pode ver o valor mais recente, mas o resultado da adição é sempre como se tivesse adicionado seu próprio valor com zero . Veja abaixo:

[21017] Adding 6 to 0!
[21020] Adding 33 to 0!
[21016] Adding 15 to 0!
[21018] Adding 24 to 0!
[21017] Got access! (0x7fe953fcb000 = 0)
[21017] Done (0x7fe953fcb000 = 6)
[21016] Got access! (0x7fe953fcb000 = 6)
[21016] Done (0x7fe953fcb000 = 15)
[21018] Got access! (0x7fe953fcb000 = 15)
[21018] Done (0x7fe953fcb000 = 24)
[21020] Got access! (0x7fe953fcb000 = 24)
[21020] Done (0x7fe953fcb000 = 33)
Sum = 33

Cada processo "vê" o último valor escrito, mas de alguma forma, depois de adicionar seu próprio componente, parece ter ignorado o valor existente. Você pode ver que cada acesso é ordenado sequencialmente, pois há um sistema de controle de acesso gerenciando quem pode gravar no espaço de memória compartilhada. O programa de teste utilizado é semelhante ao seguinte (embora eu não espere que o leitor o execute):

int main (void) {
    int local_sum = 0, gid = -1;
    volatile int *sum;

    // Fork for four processes.
    for (int i = 1; i < 4; i++) {
        if (fork() == 0) break;
    }

    // Initialize the DSM. Set GID.
    sum = (int *)dsm_init(&cfg);
    gid = dsm_get_gid();

    // Compute range.
    for (int i = 0; i < 3; i++) {
        local_sum += array[(gid * 3) + i];
    }

    // Add to local sum.
    printf("[%d] Adding %d to %d!\n", getpid(), local_sum, *sum);
    *sum = *sum + local_sum;

    // Barrier.
    dsm_barrier();

    // Print sum if process zero.
    if (gid == 0) printf("Sum = %d\n", *sum);

    // Exit.
    dsm_exit();
}

Por que cada processo pode "ver" o valor correto no endereço 0x7fe953fcb000 no espaço compartilhado, mas depois de adicionar, se comporta como se o valor naquele endereço durante a adição ainda fosse zero?

Aqui está o que me preocupa sobre esse problema:

  • Se fosse um problema de cache, como é possível imprimir o valor correto antes da operação aritmética e ainda está incorreto?
  • Estou adicionando a um valor compartilhado no heap do processo. O compilador não poderia ter assumido que o valor ali seria zero e otimizou tudo.

Existe alguma explicação para por que isso pode acontecer sob o capô? Eu tentei usar o GDB com meu programa para ver o que está acontecendo. Mas, até onde eu sei, é simplesmente mover o valor dos endereços de memória para os registradores. Ainda não vejo nenhum problema de otimização.

    
por Micrified 12.07.2018 / 19:31

1 resposta

1

Até onde eu posso ver, quatro processos são gerados em rápida sucessão, e cada um deles tenta fazer * sum + = some_value; é perfeitamente possível que todos vejam * soma como zero antes da adição.

Vamos usar uma sintaxe de assembler abstrata. A declaração C

*sum = *sum + local_sum

é compilado em

LOAD *sum into R0
LOAD local_sum into R1
ADD R1 to R0
STORE R0 to *sum

Os quatro processos correm para executar essa sequência. É perfeitamente possível que todos eles façam LOAD * somar em R0 antes de qualquer um deles ter a chance de fazer STORE R0 para * sum; de fato, dado que, como você diz, há uma chamada de sistema (e, portanto, um ponto de replanificação) desencadeada por STORE R0 para * sum, há uma chance muito boa. Você precisa sincronizar os acessos às variáveis compartilhadas, usando semáforos, por exemplo.

    
por 13.07.2018 / 03:53