Функция unmap_region
Функция unmap region о перебирает список областей памяти и освобождает принадлежащие им страничные кадры. Она принимает пять параметров: mm — указатель на дескриптор памяти; vma — указатель на дескриптор области памяти, который идентифицирует первую удаляемую область памяти; prev — указатель на область памяти, предшествующую области vma в списке областей процесса (шаги 2 И 3 В Описании фуНКЦИИ do_munmap ()); start И end — адреса, ограничивающие удаляемый интервал линейных адресов. Функция выполняет следующие действия:
1. Вызывает функцию lru add draino
2. Вызывает функцию tib gather mmu , чтобы проинициализировать переменную mmu gathers (свою у каждого процессора). Содержимое этой переменной зависит от архитектуры. Вообще говоря, переменная mmu gathers должна хранить всю информацию, необходимую для успешного обновления записей таблиц страниц процесса. В архитектуре 80x86 функция tib gather mmuo просто сохраняет значение указателя на дескриптор памяти mm в переменной mmu gathers локального процессора.
3. Сохраняет адрес переменной mmu gathers в локальной переменной tib.
4. Вызывает функцию unmap vmas для просмотра всех записей Таблицы Страниц, соответствующих интервалу линейных адресов. При наличии только одного процессора функция многократно вызывает функцию f ree swap and cache , чтобы освободить нужные страницы. В противном случае функция сохраняет указатели на дескрипторы страниц В локальной переменной mmu gathers.
5. Вызывает функцию free_pgtables (tlb, prev, start, end), пытаясь утИЛИЗИ- ровать Таблицы Страниц процесса, очищенные на предыдущем шаге.
6. Вызывает функцию tib_finish_mmu(tib, start, end) для завершения работы. Вызванная функция:
• вызывает функцию fiush_tib_mm() для очистки TLB-буфера;
• В многопроцессорных системах вызывает функцию free_pages_and_ swap cache , чтобы освободить страничные кадры, указатели на которые собраны в структуре mmu gather. Обработчик исключения "ошибка обращения к странице"
Как было сказано ранее, обработчик исключения "ошибка обращения к странице" в Linux должен отличать исключения, сгенерированные программными ошибками от тех, что вызваны попытками обращения к странице, действительно принадлежащей адресному пространству процесса, но просто еще не выделенной.
Дескрипторы областей памяти позволяют обработчику исключения эффективно выполнять свою работу. Функция do page fauit , являющаяся служебной процедурой прерывания "ошибка обращения к странице" в архитектуре 80x86, сравнивает линейный адрес, послуживший причиной возникновения ошибки, с адресами областей памяти процесса current. Таким образом, она может определить верный способ обработки исключения в соответствии со схемой,
На практике все намного сложнее, потому что обработчик этого исключения должен распознавать несколько конкретных случаев, плохо вписывающихся в общую схему. Кроме того, он должен различать несколько видов легальных попыток доступа. Идентификаторы vmalloc_fault, good_area, bad_area И no_context ЯВЛЯЮТСЯ метками в коде функции dopagef auit . Они позволяют читателю соотнести элементы блок-схемы с конкретными строчками кода.
Функция do page fauit о принимает следующие входные параметры:
- regs — адрес структуры pt regs, в которой хранится содержимое регистров микропроцессора на момент возникновения исключения;
- error code — трехбитовый код ошибки, заносимый в стек блоком управления в момент возникновения исключения биты несут следующую информацию:
• если бит 0 сброшен, значит, исключение вызвано попыткой обращения к отсутствующей странице (флаг Present в записи Таблицы Страниц сброшен); если же бит 0 установлен, значит, исключение вызвано несоответствием прав доступа;
• если бит 1 сброшен, значит, исключение вызвано попыткой чтения или выполнения; если он установлен, значит, исключение вызвано попыткой записи;
• если бит 2 сброшен, значит, исключение возникло, когда процессор работал в режиме ядра; в противном случае оно возникло в режиме пользователя.
Первой операцией функции do page fauit о является чтение линейного адреса, вызвавшего исключение. Когда оно произошло, управляющий блок процессора сохранил это значение в регистре сг2:
asm(ffmovl cr, 0" : "=r" (address)); if (regs->eflags & 0x00020200) local_irq_enable(); tsk = current;
Прочитанный линейный адрес сохраняется в локальной переменной address. Кроме того, функция обеспечивает включение локальных прерываний, если они были включены до возникновения исключения, или если процессор работал в режиме virtual-8086, и сохраняет указатели на дескриптор текущего процесса в локальной переменной tsk.
функция do page fault проверяет, принадлежит ли "ошибочный" линейный адрес четвертому гигабайту:
info.si_code = SEGV_MAPERR; if (address >= TASK_SIZE ) {
if (!(error_code & 0x101)) goto vmalloc_fault; goto bad_area_nosemaphore;}
Если исключение вызвано попыткой ядра обратиться к несуществующему страничному кадру, выполняется переход по метке vmaiioc fauit на фрагмент кода, обрабатывающий ошибки, вероятно, вызванные обращением к несмежной области памяти в режиме ядра. В противном случае делается переход по метке bad area nosemaphore на фрагмент кода, который обсуждается далее в этой главе.
Затем обработчик исключения проверяет, возникло ли оно во время выполнения ядром какой-нибудь критической процедуры или при работе какого- либо потока ядра (вспомним, что поле mm дескриптора процесса содержит null для потоков ядра):
if (in_atomic() || !tsk->mm) goto bad_area_nosemaphore;
Макрос in atomic () возвращает единицу, если исключение возникло в одной из следующих ситуаций:
-ядро выполняло какой-либо обработчик прерываний или функцию отложенного выполнения;
-ядро работало с критической областью при выключенном вытеснении в ядре.
Если ошибка обращения к странице связана с обработчиком прерываний, функцией отложенного выполнения, критической областью или потоком ядра, функция do page fauit не пытается сравнить линейный адрес с областями памяти процесса current. Потоки ядра никогда не обращаются к линейным адресам ниже task size. Обработчикам прерываний, функциям отложенного выполнения, а также коду критических областей тоже не следует обращаться к линейным адресам ниже task size, поскольку это может блокировать текущий процесс (далее в этой главе обсуждается локальная переменная info И фрагмент кода за меткой bad area nosemaphore).
Предположим, что ошибка обращения к странице не связана ни с обработчиком прерывания, ни с функцией отложенного выполнения, ни с критической областью, ни с потоком ядра. Тогда функция должна исследовать области памяти, принадлежащие процессу, чтобы определить, находится ли адрес, вызвавший ошибку, в адресном пространстве процесса. Для этой цели она должна получить mmap sem, семафор процесса для чтения/записи:
if (!down_read_trylock (&tsk->mm->mmap_sem) ) { if ((error_code & 4) == 0 &&
!search_exception_table(regs->eip)) goto bad_area_nosemaphore; down_read (& ts k->uim->uimap_sem) ;}
Если ошибки ядра и сбои аппаратуры исключены, то к моменту возникновения ошибки обращения к странице текущий процесс еще не получил семафор mmap sem ДЛЯ Записи. Тем не менее функция do page fault о должна быть уверена, что это действительно так, иначе возникнет взаимная блокировка. По этой причине функция вызывает функцию down_read_tryiock, а не down readO Если семафор закрыт, а ошибка обращения к странице произошла в режиме ядра, функция do page fault о проверяет, не возникло ли исключение из-за того, что некий линейный адрес был передан ядру в качестве параметра системного вызова. В этом случае функция do page fault о знает наверняка, что семафором владеет другой процесс, поскольку любая служебная процедура системного вызова аккуратно избегает получения семафора mmap sem для записи до обращения к адресному пространству режима пользователя. Поэтому функция ждет, когда семафор будет освобожден. В противном случае причиной исключения является программная ошибка ядра или серьезная аппаратная проблема, и функция переходит на метку bad_area_nosemaphore.
Предположим, что семафор был корректно получен для чтения. Тогда функция mmap sem ищет область памяти, содержащую адрес, вызвавший ошибку:
vma = find_vma (tsk->mm, address); if (!vma)
goto bad_area; if (vma->vm_start vm_flags & VM_GROWSDOWN)) goto bad_area; if (error_code & 4 / User Mode /
&& address + 32 esp) goto bad_area; if (expand_stack(vma, address)) goto bad_area; goto good_area;
Если флаг vm growsdown этой области установлен, а исключение возникло в режиме пользователя, функция убеждается, что адрес address меньше, чем указатель стека regs->esp (он должен быть ненамного меньше). Поскольку некоторые ассемблерные инструкции для работы со стеком (например, pusha) уменьшают содержимое регистра esp только после обращения к памяти, процессу дается 32-байтовый интервал допуска. Если адрес расположен достаточно высоко (в пределах указанного допуска), код вызывает функцию expand stacko для проверки, разрешено ли процессу расширять свой стек и свое адресное пространство. Если все в порядке, функция записывает в поле vm start дескриптора vma значение address и возвращает 0; в противном случае она возвращает -enomem.
Обратите внимание, что код, приведенный ранее, обходит проверку допуска, когда флаг vm growsdown установлен, но исключение возникло не в режиме пользователя. Эти условия означают, что ядро обращается к стеку режима пользователя, и что код должен всегда вызывать функцию expand stack.
Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | Следующая страница