Как разархивировать файл mmap'd, заменив на отображение пустых страниц - PullRequest
0 голосов
/ 24 января 2019

Есть ли способ из пользовательского пространства Linux заменить страницы сопоставленного файла (или страницы mmap в пределах определенного диапазона логических адресов) на пустые страницы (сопоставленные с /dev/null, или, возможно, одну пустую страницу, отображенную повторноповерх страниц, сопоставленных с файлом)?

Для контекста я хочу найти исправление для этой ошибки JDK:

https://bugs.openjdk.java.net/browse/JDK-4724038

Чтобы подвести итогошибка: в настоящее время невозможно отобразить файлы в Java, пока JVM не сможет собрать мусор MappedByteBuffer, который оборачивает файл mmap'd, потому что принудительное удаление файла может вызвать проблемы безопасности из-за условий гонки (например, нативный код могвсе еще пытаюсь получить доступ к тому же диапазону адресов, к которому был сопоставлен файл, и операционная система, возможно, уже сопоставила новый файл с тем же диапазоном логических адресов.

Я ищу заменить сопоставленные страницы вдиапазон логических адресов, а затем разархивируйте файл.Есть ли способ сделать это?

(Бонусные баллы, если вы знаете способ сделать это и в других операционных системах, особенно в Windows и Mac OS X.)

Обратите внимание, что это не должно быть атомарной операцией .Основная цель состоит в том, чтобы отделить отображение памяти (или замену содержимого сопоставленного файла на страницы с нулевым разбором) от закрытия файла, так как это решит множество проблем в обоих Linux (который имеетнижний предел количества файловых дескрипторов на процесс) и Windows (тот факт, что вы не можете удалить файл, пока он отображается).

ОБНОВЛЕНИЕ: см. также: Отображение памяти в файле в Windowsс атрибутом SHARE (поэтому файл не заблокирован для удаления)

Ответы [ 2 ]

0 голосов
/ 04 мая 2019

В Linux вы можете использовать mmap с MAP_FIXED, чтобы заменить отображение любым отображением, которое вы хотите.Если вы замените все сопоставление, ссылка на файл будет удалена.

0 голосов
/ 01 февраля 2019

Причина, по которой ошибка сохраняется в JDK так долго, в основном из-за состояния гонки между отключением памяти и отображением фиктивной памяти, в результате чего может отображаться какая-то другая память (возможно, с помощью собственного кода). Я ознакомился с API-интерфейсами ОС, и на уровне системного вызова не существует атомарных операций с памятью, которые бы отображали файл и отображали что-то еще по тому же адресу. Однако есть решения, которые блокируют весь процесс, вытесняя отображение из-под него.

Unmap работает корректно в финализации без охраны, потому что сборщик мусора доказал, что объект недоступен первым, поэтому расы нет.

Специальное решение для Linux:

1) vfork ()

2) отправить родителю сигнал STOP

3) разархивировать память

4) отобразить нули на свое место

5) отправить родителю сигнал CONT

6) _exit (который разблокирует родительский поток)

В Linux изменения в отображении памяти распространяются на родительский.

Код на самом деле выглядит примерно так (vfork() - помешанный человек):

int unmap(void *addr, int length)
{
    int wstatus;
    pid_t child;
    pid_t parent;
    int thread_cancel_state;
    signal_set signal_set;
    signal_set old_signal_set;

    parent = getpid();
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &thread_cancel_state);
    sigfillset(&signal_set);
    pthread_sigmask(SIG_SETMASK, &signal_set, &old_signal_set);
    if (0 == (child = vfork()) {
        int err = 0;
        kill(parent, SIGSTOP);
        if (-1 == munmap(addr, length))
            err = 1;
        else if ((void*)-1 == mmap(addr, length, PROT_NONE, MAP_ANONYMOUS, -1, 0);
            err = 1;
        kill(parent, SIGCONT);
        _exit(err);
    }
    if (child > 0)
        waitpid(child, &wstatus, 0);
    else
        wstatus = 255;

    pthread_sigmask(SIG_SETMASK, &old_signal_set, &signal_set);
    pthread_setcancelstate(thread_cancel_state, &thread_cancel_state);
    return (wstatus & 255) != 0;
}

В Windows вы можете остановить все потоки, кроме этого, используя SuspendThread, который выглядит специально для этого. Однако перечислять темы будет сложно, потому что вы боретесь против CreateThread. Вы должны запустить API перечисления потока ntdll.dll (вы не можете использовать ToolHelp здесь, поверьте мне) и SuspendThread каждый, кроме своего собственного, осторожно используя только VirtualAlloc для выделения памяти, потому что SuspendThread просто сломал все процедуры выделения кучи, и вам придется делать все это в цикле, пока вы не найдете больше.

Здесь есть некоторая рецензия, которую я не совсем чувствую, что могу точно отогнать:

http://forums.codeguru.com/showthread.php?200588-How-to-enumerate-threads-in-currently-running-process

Я не нашел никаких решений для Mac OSX.

...