OSDev

для всех
Текущее время: 30 апр 2024, 10:20

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 36 ]  На страницу Пред.  1, 2, 3, 4  След.
Автор Сообщение
СообщениеДобавлено: 20 июн 2011, 23:03 

Зарегистрирован: 18 апр 2010, 15:59
Сообщения: 155
KIV писал(а):
Выделение память блоками меньше размера страницы (типа malloc), при этом не просто округление до 4 КБ.

Меня всегда интересовало, как люди постоянно пытаются выделять память не кратную размеру страницы. Если я правильно понимаю, то x86 процессоры защищают память именно на уровне страниц, и если один "кусок" страницы выделить одному процессу, а второй - второму, то страницу придется внедрять в адресные пространства обоих процессов и соответственно, они смогут интерферировать друг с другом. То есть каждый из этих процессов сможет писать/читать данные из куска принадлежащего другому процессу, что совсем не есть гуд.

P.S.: Конечно, теоретически можно в защищенной манере выделять память не кратную размеру страницы с помощью сегментной модели памяти, но это тот еще геморрой. :(


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 21 июн 2011, 11:27 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
Система в любом случае память страницами выделяет, даже если размер не кратен. Просто она где-то должна запоминать, раз уж это поддерживается, какая часть каждой выделенной страницы выделена, а какая -- ещё нет, чтобы при следующем запросе на выделение сначала пытаться выделить новый кусок уже из "частично выделенных" страниц.

Ну а вообще, зачем понадобилось такое выделение? ИМХО, это неверный путь. Если приложению требуется расширить доступное адресное пространство, то оно должно запрашивать целые страницы, и всё. А маллок() и тому подобные вещи должна реализовывать библиотека времени выполнения самого приложения, а не ОС -- хотя б потому, что вызов сервиса ОС стоит куда дороже, чем вызов библиотечной функции.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 21 июн 2011, 12:43 

Зарегистрирован: 16 фев 2010, 22:03
Сообщения: 101
SII, то есть вы предлагаете реализовать в ядре только выделение/освобождение блоков памяти кратных размеру страницы, а работу с более мелкими блоками оставить на совести приложения?
Хорошо, но всё равно нужна хотя бы работа с кучей большими блоками. То есть функция вроде "Получить N свободных страницы виртуальной памяти". Это вынести в user mode никак нельзя, потому что ядро само должно выделять память под описатели процессов и прочие служебные структуры. Как в данном случае будет эффективней всего управлять памятью и где хранить информацию о занятой и/или свободной памяти?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 21 июн 2011, 13:46 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
В ядре фактически нужны три механизма выделения/освобождения памяти. Один из них предназначен для самого ядра; он работает с областями произвольного размера (для простоты -- кратного 8 или 16 для 32- и 64-разрядных систем соответственно). Память он берёт из заранее выделенных страниц, постоянно закреплённых в физическом ОЗУ (поскольку это куча самого ядра; в ней хранятся блоки управления задачами, устройствами и прочая внутрисистемная информация).

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

Хранение информации о памяти выполняется по-разному в зависимости от того, что именно эта информация описывает и для чего она нужна. Для хранения информации о свободной памяти в системной куче резонно использовать саму свободную память. В простейшем случае получается следующая конструкция: 1) есть переменная, содержащая адрес первого свободного блока (фактически физический, поскольку память относится к ядру системы, где обычно применяется прямое отображение виртуальных адресов на физические); 2) есть цепочка свободных блоков с размером, кратным удвоенной разрядности адреса. По смещению 0 в каждом блоке хранится адрес следующего блока в цепочке, а сразу за ним -- длина этого блока (в байтах или в единицах, кратных адреса -- неважно, это технические детали; лично я предпочитаю иметь дело с байтами). В последнем блоке адрес следующего будет равен нулю. При запросе на выделение памяти из системной кучи соответствующая подпрограмма пробегается по этой цепочке блоков и выделяет тот, размер которого не меньше запрошенного (если размеры равны, он выделяется целиком, если выделяемый больше запрошенного, от него "откусывается" кусок нужного размера). При освобождении освобождаемый блок вставляется в цепочку, при этом нужно слить его с уже имеющимися блоками, если они окажутся смежными. В целом подпрограммы выделения/освобождения получаются довольно простыми и небольшими по объёму. Вот, например, текст обеих подпрограмм из моей оси для АРМа:

Код:
; ==============================================================================
;
; ВЫДЕЛЕНИЕ БЛОКА SQA
;
; ==============================================================================
;
; Эта подпрограмма находит в SQA свободный блок подходящего размера и выделяет
; его запросившей программе. При необходимости "откусывается" часть блока боль-
; шего размера.
;
; Реальный размер выделенного блока может превышать запрошенный, поскольку он
; всегда будет кратен 8. Необходимое изменение размера подпрограмма SQA_Get вы-
; полняет сама.
;
;   На входе:
;
; - R1 - требуемый размер блока;
; - R8 - базовый адрес области переменных системы.
;
;   На выходе:
;
; - C=1 - блок не выделен из-за недостатка объёма свободной SQA или её сильной
;         фрагментации;
; - C=0 - блок успешно выделен;
;   - R0 - адрес выделенного блока (всегда кратен 8);
;   - R1 - реальный размер выделенного блока.

SQA_Get     ROUT
        add     R1, R1, #7                  ; Выравнивание длины на границу 8
        bic     R1, R1, #7
        add     R3, R8, #Free_SQA_List      ; Адрес предыдущего FSQAB

        ; Поиск свободного блока подходящего размера.
10      ldr     R0, [R3, #FSQAB_Next]       ; Адрес текущего FSQAB
        cmp     R0, #0                      ; Текущего блока нет - выделить не
        jmpeq   LR                          ; смогли, выход с установленным C
        ldr     R2, [R0, #FSQAB_Size]       ; Размер текущего блока подходит для
        subs    R2, R2, R1                  ; удовлетворения запроса?
        movlo   R3, R0                      ; Нет, переход к следующему FSQAB
        blo     %B10
        beq     %F20
 
        ; Текущий блок больше запрошенного, "откусывание" нужного куска.
        str     R2, [R0, #FSQAB_Size]       ; Сохранение размера остатка
        adds    R0, R0, R2                  ; Адрес выделенного участка и одно-
        jmp     LR                          ; временно сброс флага C

        ; Текущий блок равен запрошенному, изъятие его из списка свободных.
20      ldr     R2, [R0, #FSQAB_Next]       ; Адрес следующего свободного FSQAB
        str     R2, [R3, #FSQAB_Next]
        adds    R2, R2, #0                  ; Сброс флага C
        jmp     LR


; ==============================================================================
;
; ОСВОБОЖДЕНИЕ БЛОКА SQA
;
; ==============================================================================
;
; Эта подпрограмма возвращает в список свободных блоков SQA указанный блок. Если
; он граничит с другими соседними блоками, происходит их слияние в один более
; крупный блок.
;
; Реальный размер освобождаемого блока подпрограммой может быть увеличен, чтобы
; он стал кратен 8.
;
; Чтобы избежать неверного слияния с предыдущим блоком, адрес предыдущего на са-
; мом деле является адресом переменной Free_SQA_List, последняя размещается не-
; посредственно перед переменной Work_Flags, в которой младший бит всегда уста-
; новлен. Благодаря этому сумма адреса предыдущего блока (в данном случае пере-
; менной Free_SQA_List) и его длины (содержимого Work_Flags) никогда не может
; совпать с адресом освобождаемого блока: последний всегда кратен 8, а значит,
; его младший бит всегда равен нулю.
;
;   На входе:
;
; - R0 - адрес освобождаемого блока (должен быть кратен 8);
; - R1 - размер освобождаемого блока;
; - R8 - базовый адрес области переменных системы.

SQA_Free    ROUT

        add     R1, R1, #7                  ; Выравнивание длины на границу 8
        bic     R1, R1, #7
        add     R3, R8, #Free_SQA_List      ; Адрес предыдущего свободного блока
        add     R12, R0, R1                 ; Адрес за освобождаемым блоком

        ; Поиск точки для вставки освобождаемого блока: следующий за ним блок
        ; должен иметь больший адрес, а предшествующий - меньший, чтобы обеспе-
        ; чить слияние блоков, когда это возможно.
20      ldr     R2, [R3, #FSQAB_Next]       ; Адрес следующего свободного блока
        tst     R2, R2                      ; Он вообще существует?
        beq     %F30                        ; Нет, последним будет освобождаемый
        cmp     R2, R12                     ; Какой блок находится раньше?
        movlo   R3, R2                      ; Следующий, он становится предыду-
        blo     %B20                        ; щим; идём к следующему свободному
        bhi     %F30                        ; Следующий после освобождаемого

        ; Следующий блок сразу за освобождаемым. Слияние их в один блок.
        ldr     R12, [R2, #FSQAB_Size]      ; Вычисление длины объединённых
        add     R1, R1, R12                 ; блоков
        ldr     R2, [R2, #FSQAB_Next]       ; Новый следующий блок (несливаемый)

        ; Проверка на слияние освобождаемого блока с предыдущим.
30      ldr     R12, [R3, #FSQAB_Size]      ; Размер предыдущего блока
        add     R12, R3, R12                ; Адрес за концом предыдущего блока
        cmp     R12, R0                     ; Надо ли сливать блоки?
        beq     %F40                        ; Да

        ; Предыдущий и освобождаемый блоки не сливаются. Добавление освобождае-
        ; мого блока в список свободных.
        str     R0, [R3, #FSQAB_Next]
        str     R1, [R0, #FSQAB_Size]
        str     R2, [R0, #FSQAB_Next]
        jmp     LR

        ; Слияние освобождаемого с предыдущим.
40      str     R2, [R3, #FSQAB_Next]
        ldr     R2, [R3, #FSQAB_Size]
        add     R2, R2, R1
        str     R2, [R3, #FSQAB_Size]
        jmp     LR


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

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

А вот хранение информации о выделенной задаче виртуальной памяти, о правах доступа к ней, об отображении виртуальной памяти на физическую -- это отдельная история, причём там всё тесно связано с "идеологией" управления памятью в системе. Обсуждать одно в отрыве от другого просто смысла нет.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 21 июн 2011, 14:24 

Зарегистрирован: 16 фев 2010, 22:03
Сообщения: 101
Управление физической памятью я реализовал уже с помощью двунаправленного связанного списка (так меньше проверок и проще удалять элементы). Меня как раз интересовало управление виртуальной памятью. А именно: как лучше хранить информацию о примонтированных участках виртуального адресного пространства, чтобы потом искать куда можно примонтировать ещё памяти. При этом важен простой доступ к этому списку из других процессов, чтобы можно было выделять и освобождать участки памяти в других адресных пространствах.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 21 июн 2011, 14:32 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
А что значит "монтированная память"? Никогда такого не встречал.

Пы.Сы. Виртуальной же памятью тоже управляет система, так какого лешего нужен доступ "другим процессам"? Они вообще не должны никакого доступа иметь к внутренним структурам данных системы.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 21 июн 2011, 14:46 

Зарегистрирован: 16 фев 2010, 22:03
Сообщения: 101
Под монтированной памятью я имел ввиду страницы, которым сопоставлены физические. Правильнее, наверное, будет сказать примаппеные страницы.
Цитата:
Пы.Сы. Виртуальной же памятью тоже управляет система, так какого лешего нужен доступ "другим процессам"? Они вообще не должны никакого доступа иметь к внутренним структурам данных системы.

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 21 июн 2011, 15:59 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
mapping всегда переводилось как отображение, to map -- отображать (вообще, не люблю жаргон и не пользуюсь им; другое дело, если в русском в принципе нет адекватного и "удобопереваримого" слова -- типа там дисплея или принтера). В общем, это страницы виртуальной памяти, отображённые на страницы физической памяти. Но надо помнить (и учесть в структурах данных) ещё про необходимость закрепления отображённых страниц: это нужно, например, на время ввода-вывода, чтобы буфер, к которому обращается устройство, гарантированно находился в физической памяти. В общем случае страница может закрепляться несколько раз подряд (например, запустила задача 10 операций ввода-вывода, буферы для которых находятся хотя бы частично на этой странице, -- значит, нужно закрепить эту страницу 10 раз и уменьшать счётчик с каждой завершившейся операцией ввода-вывода, и лишь когда он обнулится, страницу можно убрать из физической памяти).

Лично у меня есть чёткое разделение между задачами (тем, что в винде называется процессами) и разделами памяти. Задача может иметь доступ к одному или нескольким разделам, причём присоединять и отсоединять их динамически. Исключением является раздел задачи, который присоединён к ней жёстко и имеется всегда; в нём находятся изменяемые данные этой задачи (в частности, стек). Выполняемый код у меня может размещаться как в разделах задач, так и в общих разделах; в частности, задача может иметь в своём личном разделе лишь данные, а весь код -- в общем разделе. С помощью соответствующих системных сервисов задача может получить доступ к другому разделу, в том числе к разделу другой задачи -- если позволяют привилегии, конечно. Загрузчик, понятное дело, будет привилегированной задачей, которая может сотворить сие. Ну а получив доступ, загрузчик может самым что ни на есть обычным образом выполнять запись в этот раздел, в том числе с помощью операций ввода-вывода.

Но вообще, если говорить о запуске новой задачи, то там имеет место тесное взаимодействие между кодом ядра (именно ядро обрабатывает системный сервис, запускающий новую задачу) и задачей-загрузчиком, поэтому всё будет несколько сложней. Насколько, пока сказать не могу: управлением памятью и задачами я пока не занимаюсь и займусь нескоро, поскольку в этом нет "производственной необходимости".

(Лирическое отступление. Поскольку я делаю ОСРВ, в ядре у меня недопустимы вещи, время выполнения которых нельзя посчитать чисто аналитически для заданной конфигурации внешних устройств и набора выполняемых задач, ведь одно из требований к настоящей ОСРВ -- полная предсказуемость времени реакции на внешнее событие. Понятное дело, что время работы загрузчика рассчитано быть не может, поскольку зависит от размещения файлов на внешних носителях, количества сбоев и т.д. и т.п. То же самое относится к ряду драйверов, например, к драйверам файловых систем -- все они должны быть задачами. В то же время драйвера с предсказуемым временем работы могут быть частью ядра -- например, GPIO, UART, SPI и т.д. Если б это была не ОСРВ, то такие драйверы, а также загрузчик можно было бы сделать частью самого ядра, как нередко и делается.)


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 22 июн 2011, 09:15 

Зарегистрирован: 10 май 2007, 11:33
Сообщения: 1206
KIV писал(а):
Под монтированной памятью я имел ввиду страницы, которым сопоставлены физические. Правильнее, наверное, будет сказать примаппеные страницы.
Нормально ты сказал - я тоже иногда говорю примонтировать и меня понимают. Еще можно говорить "отобразить", чтобы не использовать совсем уж несловарное слово "(при)маппить".

Цитата:
Я имею ввиду что можно выделить участок памяти в адресном пространстве другого процесса.
Зачем? Такое может понадобиться только для внедрения кода/данных в другой процесс. Это не является абсолютно обязательным. Прежде чем реализовать такую возможность, нужно много раз подумать, не слишком ли критично это скажется на защищенности. В принципе выделением/высвобождением памяти в адресном пространстве процесса может управлять только код этого процесса. Здесь конечно очень много архитектурноспецифичных моментов, например, распределение физической памяти между процессами (наоборот может понадобиться забирать память у других процессов, не делая их для этого активными), устойчивость любого работающего в режиме ядра потока к сбоям и т.д. Чтобы неподготовленным было легче дышать в таких условиях, может помочь механизм разделения памяти (здесь важно, что внедрение должен завершить процесс, в который оно выполняется).

Цитата:
Ситуация когда такое нужно: например, загрузчик исполняемых файлов. Сначала он создаёт пустой процесс, потом выделяет в его адресном пространстве участки памяти для кода и данных, потом копирует туда данные из файла и наконец создаёт нить на точку входа нового процесса. То есть он должен иметь возможность манипулировать чужой памятью.
Опять архитектурный момент. У меня загрузчик - это часть ядра в широком смысле (работает в режиме ядра). Загрузка кода и данных из исполняемого модуля (а точнее связывание, т.к. реальная загрузка выполняется по требованию) выполняется первичным потоком в контексте самого процесса. Из родительского процесса создается только сам процесс и его первичный поток. Имя исполняемого файла и параметры сохраняются в стеке ядра первичного потока создаваемого процесса. Это возможно, т.к. стеки ядра всех потоков сейчас размещаются в глобальной области ядра. Фактически в пространство родителя может быть отображена только страница для каталога (и то невсегда, т.к. может быть использована страница из кэша, т.е. заготовка), который заполняется только глобальными входами, описывающими область ядра, и рекурсивной ссылкой на себя.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 22 июн 2011, 11:25 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
phantom-84 писал(а):
Нормально ты сказал - я тоже иногда говорю примонтировать и меня понимают. Еще можно говорить "отобразить", чтобы не использовать совсем уж несловарное слово "(при)маппить".


А я вот не понял, о чём речь. И предполагал неправильное: что речь идёт о закреплении страниц в физической памяти (например, на время выполнения ввода-вывода), а не об отображении.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 36 ]  На страницу Пред.  1, 2, 3, 4  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB