что такое карта адресного пространства

Организация памяти процесса

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства
Управление памятью – центральный аспект в работе операционных систем. Он оказывает основополагающее влияние на сферу программирования и системного администрирования. В нескольких последующих постах я коснусь вопросов, связанных с работой памяти. Упор будет сделан на практические аспекты, однако и детали внутреннего устройства игнорировать не будем. Рассматриваемые концепции являются достаточно общими, но проиллюстрированы в основном на примере Linux и Windows, выполняющихся на x86-32 компьютере. Первый пост описывает организацию памяти пользовательских процессов.

Каждый процесс в многозадачной ОС выполняется в собственной “песочнице”. Эта песочница представляет собой виртуальное адресное пространство, которое в 32-битном защищенном режиме всегда имеет размер равный 4 гигабайтам. Соответствие между виртуальным пространством и физической памятью описывается с помощью таблицы страниц (page table). Ядро создает и заполняет таблицы, а процессор обращается к ним при необходимости осуществить трансляцию адреса. Каждый процесс работает со своим набором таблиц. Есть один важный момент — концепция виртуальной адресации распространяется на все выполняемое ПО, включая и само ядро. По этой причине для него резервируется часть виртуального адресного пространства (т.н. kernel space).

Это конечно не значит, что ядро занимает все это пространство, просто данный диапазон адресов может быть использован для мэппирования любой части физического адресного пространства по выбору ядра. Страницы памяти, соответствующие kernel space, помечены в таблицах страниц как доступные исключительно для привилегированного кода (кольцо 2 или более привилегированное). При попытке обращения к этим страницам из user mode кода генерируется page fault. В случае с Linux, kernel space всегда присутствует в памяти процесса, и разные процессы мэппируют kernel space в одну и ту же область физической памяти. Таким образом, код и данные ядра всегда доступны при необходимости обработать прерывание или системный вызов. В противоположность, оперативная память, замэппированная в user mode space, меняется при каждом переключении контекста.

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

Синим цветом на рисунке отмечены области виртуального адресного пространства, которым в соответствие поставлены участки физической памяти; белым цветом — еще не использованные области. Как видно, Firefox использовал большую часть своего виртуального адресного пространства. Все мы знаем о легендарной прожорливости этой программы в отношении оперативной памяти. Синие полосы на рисунке — это сегменты памяти программы, такие как куча (heap), стек и так далее. Обратите внимание, что в данном случае под сегментами мы подразумеваем просто непрерывные адресные диапазоны. Это не те сегменты, о которых мы говорим при описании сегментации в Intel процессорах. Так или иначе, вот стандартная схема организации памяти процесса в Linux:

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

Давным давно, когда компьютерная техника находилась в совсем еще младенческом возрасте, начальные виртуальные адреса сегментов были совершенно одинаковыми почти для всех процессов, выполняемых машиной. Из-за этого значительно упрощалось удаленное эксплуатирование уязвимостей. Эксплойту часто необходимо обращаться к памяти по абсолютным адресам, например по некоторому адресу в стеке, по адресу библиотечной функции, и тому подобное. Хакер, рассчитывающий осуществить удаленную атаку, должен выбирать адреса для обращения в слепую в расчете на то, что размещение сегментов программы в памяти на разных машинах будет идентичным. И когда оно действительно идентичное, случается, что людей хакают. По этой причине, приобрел популярность механизм рандомизации расположения сегментов в адресном пространстве процесса. Linux рандомизирует расположение стека, сегмента для memory mapping, и кучи – их стартовый адрес вычисляется путем добавления смещения. К сожалению, 32-битное пространство не очень-то большое, и эффективность рандомизации в известной степени нивелируется.

В верхней части user mode space расположен стековый сегмент. Большинство языков программирования используют его для хранения локальных переменных и аргументов, переданных в функцию. Вызов функции или метода приводит к помещению в стек т.н. стекового фрейма. Когда функция возвращает управление, стековый фрейм уничтожается. Стек устроен достаточно просто — данные обрабатываются в соответствии с принципом «последним пришёл — первым обслужен» (LIFO). По этой причине, для отслеживания содержания стека не нужно сложных управляющих структур – достаточно всего лишь указателя на верхушку стека. Добавление данных в стек и их удаление – быстрая и четко определенная операция. Более того, многократное использование одних и тех же областей стекового сегмента приводит к тому, что они, как правило, находятся в кеше процессора, что еще более ускоряет доступ. Каждый тред в рамках процесса работает с собственным стеком.

Возможна ситуация, когда пространство, отведенное под стековый сегмент, не может вместить в себя добавляемые данные. В результате, будет сгенерирован page fault, который в Linux обрабатывается функцией expand_stack(). Она, в свою очередь, вызовет другую функцию — acct_stack_growth(), которая отвечает за проверку возможности увеличить стековый сегмент. Если размер стекового сегмента меньше значения константы RLIMIT_STACK (обычно 8 МБ), то он наращивается, и программа продолжает выполняться как ни в чем не бывало. Это стандартный механизм, посредством которого размер стекового сегмента увеличивается в соответствии с потребностями. Однако, если достигнут максимально разрещённый размер стекового сегмента, то происходит переполнение стека (stack overflow), и программе посылается сигнал Segmentation Fault. Стековый сегмент может увеличиваться при необходимости, но никогда не уменьшается, даже если сама стековая структура, содержащаяся в нем, становиться меньше. Подобно федеральному бюджету, стековый сегмент может только расти.

Динамическое наращивание стека – единственная ситуация, когда обращение к «немэппированной» области памяти, может быть расценено как валидная операция. Любое другое обращение приводит к генерации page fault, за которым следует Segmentation Fault. Некоторые используемые области помечены как read-only, и обращение к ним также приводит к Segmentation Fault.

Под стеком располагается сегмент для memory mapping. Ядро использует этот сегмент для мэппирования (отображания в память) содержимого файлов. Любое приложение может воспользоваться данным функционалом посредством системного вызовома mmap() (ссылка на описание реализации вызова mmap) или CreateFileMapping() / MapViewOfFile() в Windows. Отображение файлов в память – удобный и высокопроизводительный метод файлового ввода / вывода, и он используется, например, для загрузки динамических библиотек. Существует возможность осуществить анонимное отображение в память (anonymous memory mapping), в результате чего получим область, в которую не отображен никакой файл, и которая вместо этого используется для размещения разного рода данных, с которыми работает программа. Если в Linux запросить выделение большого блока памяти с помощью malloc(), то вместо того, чтобы выделить память в куче, стандартная библиотека C задействует механизм анонимного отображения. Слово «большой», в данном случае, означает величину в байтах большую, чем значение константы MMAP_THRESHOLD. По умолчанию, это величина равна 128 кБ, и может контролироваться через вызов mallopt().

Кстати о куче. Она идет следующей в нашем описании адресного пространства процесса. Подобно стеку, куча используется для выделения памяти во время выполнения программы. В отличие от стека, память, выделенная в куче, сохранится после того, как функция, вызвавшая выделение этой памяти, завершится. Большинство языков предоставляют средства управления памятью в куче. Таким образом, ядро и среда выполнения языка совместно осуществляют динамическое выделение дополнительной памяти. В языке C, интерфейсом для работы с кучей является семейство функций malloc(), в то время как в языках с поддержкой garbage collection, вроде C#, основной интерфейс – это оператор new.

Если текущий размер кучи позволяет выделить запрошенный объем памяти, то выделение может быть осуществлено средствами одной лишь среды выполнения, без привлечения ядра. В противном случае, функция malloc() задействует системный вызов brk() для необходимого увеличения кучи (ссылка на описание реализации вызова brk). Управление памятью в куче – нетривиальная задача, для решения которой используются сложные алгоритмы. Данные алгоритмы стремятся достичь высокой скорости и эффективности в условиях непредсказуемых и хаотичных пэттернов выделения памяти в наших программах. Время, затрачиваемое на каждый запрос по выделению памяти в куче, может разительно отличаться. Для решения данной проблемы, системы реального времени используют специализированные аллокаторы памяти. Куча также подвержена фрагментированию, что, к примеру, изображено на рисунке:

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

Наконец, мы добрались до сегментов, расположенных в нижней части адресного пространства процесса: BSS, сегмент данных (data segment) и сегмент кода (text segment). BSS и data сегмент хранят данные, соответствующий static переменным в исходном коде на C. Разница в том, что в BSS хранятся данные, соответствующие неинициализированным переменным, чьи значения явно не указаны в исходном коде (в действительности, там хранятся объекты, при создании которых в декларации переменной либо явно указано нулевое значение, либо значение изначально не указано, и в линкуемых файлах нет таких же common символов, с ненулевым значением. – прим. перевод.). Для сегмента BSS используется анонимное отображение в память, т.е. никакой файл в этот сегмент не мэппируется. Если в исходном файле на C использовать int cntActiveUsers, то место под соответствующий объект будет выделено в BSS.

В отличии от BSS, data cегмент хранит объекты, которым в исходном коде соответствуют декларации static переменных, инициализированных ненулевым значением. Этот сегмент памяти не является анонимным — в него мэппируется часть образа программы. Таким образом, если мы используем static int cntWorkerBees = 10, то место под соответствующий объект будет выделено в data сегменте, и оно будет хранить значение 10. Хотя в data сегмент отображается файл, это т.н. «приватный мэппинг» (private memory mapping). Это значит, что изменения данных в этом сегменте не повлияют на содержание соответствующего файла. Так и должно быть, иначе присвоения значений глобальным переменным привели бы к изменению содержания файла, хранящегося на диске. В данном случае это совсем не нужно!

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

Мы можем посмотреть, как используются области памяти процесса, прочитав содержимое файла /proc/pid_of_process/maps. Обратите внимание, что содержимое самого сегмента может состоять из различных областей. Например, каждой мэппируемой в memory mapping сегмент динамической библиотеке отводится своя область, и в ней можно выделить области для BSS и data сегментов библиотеки. В следующем посте поясним, что конкретно подразумевается под словом “область”. Учтите, что иногда люди говорят “data сегмент”, подразумевая под этим data + BSS + heap.

Можно использовать утилиты nm и objdump для просмотра содержимого бинарных исполняемых образов: символов, их адресов, сегментов и т.д. Наконец, то, что описано в этом посте – это так называемая “гибкая” организация памяти процесса (flexible memory layout), которая вот уже несколько лет используется в Linux по умолчанию. Данная схема предполагает, что у нас определено значение константы RLIMIT_STACK. Когда это не так, Linux использует т.н. классическую организации, которая изображена на рисунке:

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

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

Источник

Русские Блоги

Понимание адресного пространства процесса и пространства виртуальной памяти

Понимание адресного пространства процесса и пространства виртуальной памяти

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

Один Механизм раннего распределения памяти

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

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

Рисунок 1 Метод раннего распределения памяти

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

Проблема 2: низкая эффективность использования памяти. Когда работают и A, и B, если пользователь снова запускает программу C, а программе C требуется 20 МБ памяти для запуска, и в это время в системе доступно только 8 МБ свободного места, поэтому система должна быть в Выберите одну из запущенных программ, чтобы временно скопировать данные программы на жесткий диск, освободить часть пространства для использования программой C, а затем загрузить все данные программы C в память для запуска. Вполне возможно, что в этом процессе загружается и выгружается большой объем данных, что приводит к очень низкой эффективности.

Два Сегментированный

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

При создании процесса операционная система выделяет для процесса размер 4 ГБ. виртуальный Адресное пространство процесса. Причина, по которой это 4 ГБ, заключается в том, что в 32-разрядной операционной системе указатель имеет длину 4 байта, а адресная способность 4-байтового указателя составляет от 0x00000000 до 0xFFFFFFFF, а максимальное значение 0xFFFFFFFF представляет емкость 4 ГБ. В отличие от виртуального адресного пространства существует также физическое адресное пространство, которое соответствует реальной физической памяти. Если на вашем компьютере установлено 512 МБ памяти, диапазон этого физического адресного пространства составляет 0x00000000

0x1FFFFFFF. Когда операционная система сопоставляет виртуальный адрес с физическим адресом, он может сопоставить только этот диапазон, а операционная система может сопоставить только этот диапазон. При создании процессов каждый процесс будет иметь собственное виртуальное адресное пространство размером 4 ГБ. Следует отметить, что это адресное пространство размером 4 ГБ является «виртуальным», а не реальным, и каждый процесс может получать доступ к данным только в своем собственном виртуальном адресном пространстве и не может получать доступ к данным в других процессах. Это достигается этим методом. Устраните изоляцию между процессами. Свободны ли эти 4 ГБ приложений виртуального адресного пространства? К сожалению, в системе Windows это виртуальное адресное пространство разделено на 4 части: область нулевого указателя, пользовательская область, запрещенная область 64 КБ и область ядра. Приложение может использовать только пользовательскую область, которая составляет около 2 ГБ (можно настроить до 3 ГБ). Область ядра составляет 2 ГБ. Область ядра сохраняет планирование системных потоков, управление памятью, драйверы устройств и другие данные. Эта часть данных используется всеми процессами, но приложения не могут получить к ней прямой доступ.

Причина, по которой люди создают виртуальное адресное пространство, состоит в том, чтобы решить проблему изоляции адресного пространства процесса. Однако, если программа хочет выполняться, она должна выполняться в реальной памяти, поэтому между виртуальным адресом и физическим адресом должна быть установлена ​​связь. Таким образом, через механизм отображения, когда программа обращается к значению адреса в виртуальном адресном пространстве, это эквивалентно доступу к другому значению в физическом адресном пространстве. Люди думают о Сегментированный (Sagmentation), его идея состоит в том, чтобы сделать однозначное сопоставление между виртуальным адресным пространством и физическим адресным пространством. Например, пространство 10 МБ в виртуальном адресном пространстве отображается на пространство 10 МБ в физическом адресном пространстве. Эту идею нетрудно понять. Операционная система гарантирует, что адресное пространство различных процессов отображается в разные области физического адресного пространства, так что каждый процесс, наконец, обращается к

Метод отображения памяти.

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

Рисунок 2 Метод сопоставления сегментированной памяти

Хотя этот метод сегментированного отображения решает первую и третью проблемы, упомянутые выше, он не решает вторую проблему, то есть эффективность использования памяти. В методе сегментированного отображения вся программа каждый раз переключается в память и выгружается из нее, что приводит к большому количеству операций доступа к диску, что приводит к низкой эффективности. Поэтому этот метод сопоставления все еще немного груб, а степень детализации относительно велика. Фактически, работа программы имеет характеристики локальности. В определенный период времени программа получает доступ только к небольшой части данных программы, то есть большая часть данных программы не будет использоваться в течение определенного периода времени. Исходя из этой ситуации, люди думают о методе сегментации памяти и отображения с меньшей степенью детализации. Этот метод является Пагинация (Paging) 。

три Пагинация

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

После создания структуры данных, необходимой для виртуального адресного пространства, процесс начинает читать первую страницу PE-файла. Первая страница PE-файла содержит такую ​​информацию, как заголовок PE-файла и таблица сегментов. В соответствии с заголовком файла и таблицей сегментов процесс сопоставляет все сегменты PE-файла с соответствующими страницами в виртуальном адресном пространстве (в PE-файле). Длина сегмента кратна длине страницы). В настоящее время реальные инструкции и данные PE-файла не загружены в память.Операционная система просто устанавливает взаимосвязь между PE-файлом и страницей в виртуальном адресном пространстве процесса на основе заголовка PE-файла и другой информации. Когда ЦП хочет получить доступ к определенному виртуальному адресу, используемому в программе, когда ЦП обнаруживает, что с адресом не связан физический адрес, ЦП думает, что страница, на которой расположен виртуальный адрес, является пустой страницей, а ЦП считает, что это ошибка страницы ( Page Fault), ЦП также знает, что операционная система не выделила память для PE-страницы, и ЦП вернет управление операционной системе. Затем операционная система выделяет страницу в физическом пространстве для PE-страницы, а затем сопоставляет физическую страницу с виртуальной страницей в виртуальном пространстве, а затем возвращает управление процессу, и процесс перезапускается с позиции, где только что произошла ошибка страницы. выполненный. Поскольку в это время для этой страницы PE-файла выделена память, сбой страницы не произойдет. При выполнении программы сбои страниц будут продолжать возникать, и операционная система также будет выделять соответствующие физические страницы для процесса, чтобы удовлетворить потребности процесса.

Основная идея метода разбиения на страницы заключается в том, что когда исполняемый файл выполняется на x-й странице, для x-й страницы выделяется страница памяти y, а затем эта страница памяти добавляется в таблицу сопоставления виртуального адресного пространства процесса. Эта таблица сопоставления эквивалентна В функции y = f (x). Приложение может получить доступ к странице y, связанной со страницей x, через эту таблицу сопоставления.

подводить итоги:

Источник

Сегментация памяти (Схема памяти компьютера)

Представляю, Вам, перевод статьи одного из разработчиков PHP, в том числе версии 7 и выше, сертифицированного инженера ZendFramework. В данный момент работает в SensioLabs и большую часть занимается низкоуровневыми вещами, в том числе программированием в С под Unix. Оригинал статьи здесь.

Ошибка Сегментации: (Компьютерная верстка памяти)

Несколько слов, о чем эта запись в блоге

Я планирую в будущем писать технические статьи о PHP, связанные с глубоким пониманием памяти. Мне нужно, чтобы мои читатели имели такие знания, которые им помогут понять некоторые концепции дальнейшего объяснения. Для того, чтобы ответить на этот вопрос, нам придется перемотать время назад в 1960-е года. Я собираюсь объяснить вам, как работает компьютер, а точнее, как происходит доступ к памяти в современном компьютере, а затем вы поймете, из-за чего происходит это странное сообщение об ошибке — Segmentation Fault.

То, что вы будете читать здесь, краткое изложение основ дизайна компьютерной архитектуры. Я не буду заходить слишком далеко, если это не нужно, и буду использовать хорошо известные формулировки, так что, кто работает с компьютером каждый день может понять такие важные понятия о том, как работает ПК. Существует много книг о компьютерной архитектуре. Если вы хотите углубиться дальше в этой теме, я предлагаю вам достать некоторые из них и начать читать. Кроме того, откройте исходный код ядра ОС и изучите его, будь то ядро Linux, или любое другое.

Немного истории computer science

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

Источник

Карта распределения адресного пространства памяти

В разрабатываемой МПС объем адресного пространства памяти (АПП) намного превышает объем физически устанавливаемой памяти. В связи с этим распределение АПП производится, исходя из критерия минимума сложности дешифратора адреса ОЗУ и ПЗУ. При распределении АПП необходимо также учитывается, что после подачи питающего напряжения +5 и окончания действия сигнала RESET, выдаваемого схемой начального сброса на МП, последний переходит реальный режим адресации памяти и считывает первую команду программы из ячейки памяти с адресом FFFFF0h. Так как исполняемая программа в реализуемой МПС размещена в ИС ПЗУ, то под ПЗУ отведена область АПП, включающую в себя этот адрес. Содержимое регистров после аппаратного сброса:

FLAGS=0002h, MSW=FFF0h, CS=F000h, IP=FFF0h, DS=ES=SS=0000h.

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

В соответствии с рисунком 23, диапазон адресов АПП с 0…524287 (7FFFFh), используется для адресации имеющихся в МПС ячеек ОЗУ. В виду того, что диапазон адресов с 0 по 1023 используется подсистемой прерывания для размещения адресов подпрограмм обработки прерываний, использование этих ячеек ОЗУ для других целей нецелесообразно. Верхняя часть ОЗУ отведена под стековую память.

Диапазон адресов 983040…1048575 используется для адресации ячеек ПЗУ емкостью 64Кб. Первая (исполняемая) команда основной программы размещается по адресу FFFFFh минус размер модуля ПЗУ в байтах, при этом в ячейке по адресу запуска системы (FFFFF0h) размещается команда JMP START.

Основная же часть АПП ввиду небольших размеров ОЗУ и ПЗУ в МПС не используется.

Реализация дешифратора адреса ОЗУ и ПЗУ имеет вид, приведенный на рисунке 24.

что такое карта адресного пространства. Смотреть фото что такое карта адресного пространства. Смотреть картинку что такое карта адресного пространства. Картинка про что такое карта адресного пространства. Фото что такое карта адресного пространства

На рисунке 24 сигналы CSROM и CSRAM обозначают, соответственно, сигналы выбора модулей ПЗУ и ОЗУ.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *