Удалить разделительную память posix, когда она не используется?

Есть ли какой-либо способ, специфичный для linux или нет, чтобы сегменты разделяемой памяти posix (полученные с помощью shm_open()) удалялись, когда ни один процесс не использует их. то есть подсчитать их ссылку и удалить их, когда эталон становится 0

Несколько примечаний:

  • Установление обработчика atexit для их удаления не работает, если программа сработает.

  • В настоящее время, специфический для Linux способ, я вставляю pid в название сегмента и пытаюсь найти неиспользуемые сегменты, ходя /dev/shm во внешней программе. Который имеет недостаток в необходимости периодически очищать их извне довольно хакерским способом.

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

Ответ 1

Нет - в Linux, ядро ​​не содержит ничего, что могло бы это сделать. Для некоторых приложений для вызова shm_unlink() в какой-то момент нужно избавиться от сегмента разделяемой памяти.

Ответ 2

Если в вашей программе есть момент, когда хорошо известно, что все процессы, которые должны открыть сегмент разделяемой памяти, уже сделали это, вы можете безопасно отменить его. Unlinking удаляет объект из глобального пространства имен, но он задерживается до тех пор, пока существует хотя бы один процесс, который сохраняет его дескриптор файла открытым. Если после этой точки происходит сбой, дескриптор файла автоматически закрывается и счетчик ссылок уменьшается. После того, как открытые дескрипторы незаблокированного блока разделяемой памяти не будут сохранены, он будет удален.

Это полезно в следующем сценарии: процесс создает блок разделяемой памяти, отключает его, а затем вилки. Ребенок наследует дескриптор файла и может использовать блок разделяемой памяти для связи с родителем. Как только оба процесса завершатся, блок автоматически удаляется, так как оба дескриптора файла закрываются.

При отсоединении блок разделяемой памяти недоступен для других процессов, чтобы открыть его. Между тем, если вы используете shm_open() с тем же именем, что и несвязанный блок, вместо него будет создан новый и полностью другой блок разделяемой памяти.

Ответ 3

Я нашел способ, используя системную команду и команду "fuser" Linux, которые позволяют перечислять процессы, которые открывали файл. Таким образом, вы можете проверить, используется ли файл общей памяти (расположенный в /dev/shm ) и удалить его, если нет. Обратите внимание, что операции check/delete/create должны быть заключены в критический раздел между процессами используя именованный мьютекс или именованный семафор или блокировку файла.

        std::string shm_file = "/dev/shm/" + service_name + "Shm";
        std::string cmd_line = "if [ -f " + shm_file + " ] ; then if ! fuser -s " + shm_file + " ; then rm -f " + shm_file + " ; else exit 2 ; fi else exit 3 ; fi";
        int res = system(cmd_line.c_str());
        switch (WEXITSTATUS(res)) {
        case 0: _logger.warning ("The shared memory file " + shm_file + " was found orphan and is deleted");         break;
        case 1: _logger.critical("The shared memory file " + shm_file + " was found orphan and cannot be deleted");  break;
        case 2: _logger.trace   ("The shared memory file " + shm_file + " is linked to alive processes");            break;
        case 3: _logger.trace   ("The shared memory file " + shm_file + " is not found");                            break;
        }

Ответ 4

Для общей памяти, созданной с помощью sysV API, возможно такое поведение. Только в Linux. Это не общая память POSIX, но может работать для вас.

В книге Интерфейс программирования Linux один из возможных параметров для shmctl() описан следующим образом.

IPC_RMID Отметьте сегмент разделяемой памяти и связанные с ним файлы shmid_ds структура данных для удаления. Если в настоящее время нет процессов сегмент прилагается, удаление немедленно; в противном случае сегмент удалены после того, как все процессы отделились от него (т.е. когда значение поля shm_nattch в структуре данных shmid_ds падает до 0). В некоторых приложениях мы можем убедиться, что общая память сегмент аккуратно очищается при завершении заявки путем маркировки он для удаления сразу после того, как все процессы привязали его к их виртуальное адресное пространство с помощью shmat(). Это аналогично отменив файл, как только weve открыл его. В Linux, если общий сегмент был отмечен для удаления с использованием IPC_RMID, но еще не был удален, потому что какой-то процесс все еще привязан, тогда возможно для другого процесса для присоединения этого сегмента. Однако это поведение не переносимо: большинство реализаций UNIX предотвращают прикрепляется к сегменту, отмеченному для удаления. (SUSv3 молчал о том, что поведение должно происходить в этом сценарии.) Несколько Linux-приложений имеют зависят от этого поведения, поэтому Linux не был изменен в соответствии с другими реализациями UNIX.

Ответ 5

Не могли бы вы использовать глобальный счетный семафор для подсчета ссылок? Оберните прикрепление и отсоедините вызовы, чтобы семафор увеличивался при добавлении в память и уменьшался при отсоединении. Отпустите сегмент, когда отрыв уменьшает семафор до нуля.

Ответ 6

Не уверен, если ниже работает или возможно. Но моя попытка.

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

т.е.:

/proc/sys/kernel/core_pattern  to  /path/to/Myprogram %p

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

см.

man 5 core.  for more information. 

Надеюсь, это поможет в некоторой степени.

Ответ 7

Предположим, что наиболее сложный случай:

  • У вас есть несколько процессов, сообщающих через общую память.
  • Они могут начинаться и заканчиваться в любое время, даже несколько раз. Это означает, что нет мастер-процесса, и нет специального "первого" процесса, который может инициализировать разделяемую память.
  • Это означает, что, например, нет смысла безопасно отключать разделяемую память, поэтому ни Сергей, ни Hristo's отвечает на работу.

Я вижу два возможных решения и приветствую отзывы о них, потому что интернет ужасно молчал по этому вопросу:

  • Сохраните pid (или более конкретный идентификатор процесса, если он у вас есть) последнего процесса, который написал в разделяемую память в общей памяти как блокировку. Тогда вы могли бы сделать это. например, следующий псевдо-код:

    int* pshmem = open shared memory()
    
    while(true) 
        nPid = atomic_read(pshmem)
        if nPid = 0 
           // your shared memory is in a valid state
           break
        else 
           // process nPid holds a lock to your shared memory
           // or it might have crashed while holding the lock
           if process nPid still exists 
             // shared memory is valid
             break
           else 
             // shared memory is corrupt
             // try acquire lock 
             if atomic_compare_exchange(pshmem, nPid, my pid) 
                // we have the lock
                reinitialize shared memory
                atomic_write(pshem, 0) // release lock
             else 
                // somebody else got the lock in the meantime
                // continue loop
    

    Это подтверждает, что последний писатель не умирал во время написания. Общая память по-прежнему сохраняется дольше, чем любой из ваших процессов.

  • Используйте блокировку файла чтения/записи, чтобы узнать, является ли какой-либо процесс первым процессом, открывающим объект общей памяти. Первый процесс может затем повторно инициализировать общую память:

    // try to get exclusive lock on lockfile
    int fd = open(lockfile, O_RDONLY | O_CREAT | O_EXLOCK | O_NONBLOCK, ...)
    if fd == -1
        // didn't work, somebody else has it and is initializing shared memory
        // acquire shared lock and wait for it
        fd = open(lockfile, O_RDONLY | O_SHLOCK)
        // open shared memory
    else 
        // we are the first
        // delete shared memory object
        // possibly delete named mutex/semaphore as well
    
        // create shared memory object (& semaphore)
        // degrade exclusive lock to a shared lock
        flock(fd, LOCK_SH)
    

    Блокировки файлов кажутся единственным (?) механизмом в системах POSIX, который автоматически очищается, когда процесс умирает. К сожалению, список предостережений для их использования очень, очень длинный. Алгоритм предполагает, что flock поддерживается на базовой файловой системе, по крайней мере, на локальной машине. Алгоритм не волнует, действительно ли блокировки видны другим процессам в файловых системах NFS или нет. Они должны быть видимыми только для всех процессов, обращающихся к общему объекту памяти.