#windows #dll
Среди преимуществ использования динамических библиотек перед статическими значится пункт экономии ресурсов системы, т.е. библиотека загружается в памяти единожды и используется сразу несколькими процессами (в смысле если библиотека уже загружена другим процессом, повторная загрузка выполняться не будет). Отсюда вопрос: куда же на самом деле загружается динамическая библиотека, если Windows позволяет использовать загруженный код нескольким процессам? В адресное пространство процесса? Но ведь тогда, когда процесс загрузивший DLL прекратить работу и выгрузится из памяти операционной системой, другой процесс потерпит фиаско!
Ответы
Ответ 1
Куда же на самом деле загружается динамическая библиотека, если Windows позволяет использовать загруженный код нескольким процессам? В адресное пространство процесса? Но ведь тогда, когда процесс загрузивший DLL прекратить работу и выгрузится из памяти операционной системой, другой процесс потерпит фиаско! Верно. Поэтому для библиотек используется такой объект операционной системы, как файловое отображение. Упрощённо говоря, это буфер в физической памяти, отдельные части которого программы могут отображать в своё адресное пространство. Стоит отметить, что непосредственно буфер они не видят, он вне зоны их досягаемости; для создания отображения программы должны попросить об этом операционную систему. Этот буфер может быть привязан к какому-то файлу, тогда его содержимое будет равно содержимому файла, и все изменения на одной стороне будет транслироваться на другую. Также буфер может быть открыт в гибридном режиме, при котором его начальное содержимое копируется из файла, но дальнейшие изменения обратно не транслируются. Официально это называется Copy-on-Write, копирование при записи, — то есть при записи в участок файла поверх него как бы создаётся перехватывающая заплатка с копией содержимого этого участка. Но самое главное — файловое отображение, как и файл, не принадлежит какому-то отдельному процессу, то есть это разделяемый ресурс. Если несколько программ запрашивают отображение одного и того же файла с одними и теми же правами доступа, они будут работать с одним и тем же отображением, не подозревая об этом. Соответственно и уничтожение этого буфера с закрытием соответствующего файла произойдёт только после закрытия всех его экземпляров у всех процессов-пользователей. С точки зрения операционной системы это выглядит так: Виртуальная памяти процессов — физическая память — файл на диске. Иллюстрация взята из статьи «File Mapping» в MSDN Теперь о том, как это применимо к библиотекам и многократно запущенным программам. Сначала загрузчик просто открывает исполняемый файл. Затем он читает его заголовок и для каждой его секции создаёт отдельное файловое отображение с правами, указанными в заголовке этой секции: для кода, ресурсов, константных данных и изменяемых данных. Причём последнее отображается в режиме Copy-on-Write, чтобы у каждой программы библиотека обладала собственным набором переменных. Выше я написал упрощённое объяснение, необходимое для понимания концепции. Теперь расскажу о том, как всё это работает на низком уровне. Как я уже писал, работа буфера обеспечена куском физической памяти, фрагменты которого отображаются на виртуальную память процессов. Иными словами, когда программа просит операционную систему создать отображение с такими-то правами доступа для такого-то участка такого-то файла, операционная система модифицирует некоторые записи в таблице соответствия, в результате чего какой-то ранее нечитаемый диапазон страниц адресного пространства начинает отображаться на участок внутри буфера. Это возможно благодаря принципам организации работы виртуальной памяти: (Иллюстрация взята из ответа на вопрос «Какую модель памяти сегментную или страничную использует windows, linux, macos?») Именно указатель на начало этого диапазона и возвращают функции MapViewOfFile() на Windows и mmap() на Linux.
Комментариев нет:
Отправить комментарий