Указатели в общей памяти

Я использую общую память (shm_open/mmap) для отслеживания некоторого состояния. В моей общей памяти у меня есть структура:

typedef struct fim_t {
    uint64_t num_procs;
    uint64_t num_numa;
    int64_t *numa_nodes[MAX_FIM_NUMA];
    int64_t procs[MAX_FIM_PROC];
}fim_t;

Я хочу загрузить идентификаторы процессов в массиве procs, а затем указать массив numa_nodes на значения массива procs, чтобы я мог манипулировать этим значением в одном месте и изменять его во всех ссылках. Насколько я понимаю, установка ссылок numa_nodes на адреса массива procs не должна быть нарушением доступа к памяти, поскольку их адреса полностью находятся в сегменте разделяемой памяти. Однако я получаю ошибку сегмента, когда пытаюсь получить доступ к значению, которое говорит мне, что мое предыдущее утверждение должно быть ложным.

Вот пример кода:

int main(){
    int fd;
    int init_flag = 0;
    if((fd = shm_open("fim", O_RDWR | O_CREAT | O_EXCL, S_IRWXU)) > 0){
        printf("creating shared memory\n");
        init_flag = 1;
    } else {
        printf("opening shared memory\n");
        fd = shm_open("fim", O_RDWR, S_IRWXU);
    }
    if (-1 == fd) {
        printf("fd is negative\n");
        abort();
    }
    if ((1 == init_flag) && -1 == ftruncate(fd, sizeof(fim_t))){
        printf("ftruncate failed %d\n", errno);
        abort();
    }

    fim_t *fim = mmap(NULL, sizeof(fim_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(MAP_FAILED == fim){
        printf("mmap failed\n");
        abort();
    }
    if(init_flag){
        fim->num_procs = 1;
        my_rank = 0;
        for(int x=0;x<MAX_FIM_PROC;x++){
            fim->procs[x] = 0;
        }
        fim->numa_nodes[0] = &(fim->procs[0]);
    } else {
        my_rank = __sync_fetch_and_add(&(fim->num_procs),1);
        fim->procs[my_rank] = my_rank;
        fim->numa_nodes[0] = &(fim->procs[my_rank]);
    }
    printf("my rank is: %"PRId64"\n",my_rank);
    sleep(5);
    printf("my numa val is %"PRId64"\n",*fim->numa_nodes[0]);
    printf("rank %"PRId64" is going down\n", my_rank);
    // SHUTDOWN
    uint64_t active = __sync_sub_and_fetch(&(fim->num_procs),1);
    printf("num active is now %"PRId64"\n", active);
    close(fd);
    shm_unlink("fim");
    return 0;
}

Я ожидаю / надеюсь, что произойдет то, что я запустил один процесс, а затем сразу же запустил другой, и первый процесс напечатал "my numa val is 1" (из-за второго процесса, установившего значение numa_node[0]), и оба завершили работу корректно. Тем не менее, второй процесс работает нормально, но в первом процессе происходит сбой сегмента (доступ к памяти) в операторе печати для numa_node[0] (после сна).

Итак, вот мой вопрос: я делаю что-то не так или мой подход не работает? Если это неработоспособно, есть ли другой способ добиться результата, который я ищу?

2 ответа

Решение

Насколько я понимаю, установка ссылок numa_nodes на адреса массива procs не должна быть нарушением доступа к памяти, поскольку их адреса полностью находятся в сегменте разделяемой памяти.

Проблема в том, что разные процессы отображают общую память по разным адресам.

fim_t *fim = mmap(NULL, sizeof(fim_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

fim будет иметь разные значения в разных процессах. Распечатайте это, чтобы проверить это.

Это вызывает указатели на int64_t procs[MAX_FIM_PROC] элементы должны быть разными в разных процессах.

fim is <addr1> in process 1
fim is <addr2> in process 2

&fim->procs[0] will be different in two processes

&fim->procs[0] is <addr1> + <offset> in process 1
&fim->procs[0] is <addr2> + <offset> in process 2

Поскольку это разные значения, они не могут быть разделены между процессами. Действительный указатель в одном процессе не будет действительным в другом процессе.

Есть два возможных решения этого.

  1. Принудительно сопоставить разделяемую память с одним и тем же адресом во всех процессах. mmap имеет возможность сделать это. Затем вы можете делиться указателями на элементы в общей памяти между процессами.
  2. Не делитесь указателями в общей памяти. Вместо этого делитесь индексами.

Вы ничего не сделали для того, чтобы все пользователи разделяемой памяти отображали ее по одному и тому же виртуальному адресу. Некоторые системы * nix будут делать это по умолчанию, но большинство - нет.

Либо попробуйте сопоставить свой сегмент по фиксированному адресу (и справиться с ошибкой - это может не сработать) - либо сохранить смещения в общей памяти, а не в реальных указателях.

Другие вопросы по тегам