Обработка ошибочного адреса, входящего в адресное пространство
Если адрес address принадлежит адресному пространству процесса, функция do page fault о выполняет операторы после метки good_area:
good_area:
info.si_code = SEGV_ACCERR; write = 0;
if (error_code & 2) { / write access / if (! (vma->vm_flags & VM_WRITE)) goto bad_area; write++;
} else / read access /
if ((error_code & 1) I| !(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area;
Если исключение было вызвано попыткой записи, функция проверяет, доступна ли область для записи. Если нет, выполняется код за меткой bad area; если область доступна для записи, функция записывает 1 в локальную переменную write.
Если исключение было вызвано попыткой чтения или выполнения, функция проверяет, присутствует ли страница в оперативной памяти. В этом случае исключение было сгенерировано, потому что процесс попытался обратиться
к привилегированному страничному кадру (у которого сброшен флаг user/supervisor) в режиме пользователя, и поэтому функция переходит на метку bad area7. Если страница отсутствует, функция также проверяет, доступна ли область памяти для чтения или выполнения.
Если права доступа к области памяти не конфликтуют с типом обращения, вызвавшего исключение, вызывается функция handle mm fauitо, которая выделяет новый страничный кадр:
survive:
ret = handle_mm_fault(tsk->mm, wia, address, write); if (ret == VM_FAULT_MINOR || ret == VM_FAULT_MAJOR) {
if (ret == VM_FAULT_MINOR) tsk->min_flt++; else tsk->maj_flt++;
up_read (& t s k- >mm- >mmap_s em) ;
return;}
Функция handie_mm_fauit возвращает значение vm_fault_minor или vm fault major, если ей удалось выделить процессу новый страничный кадр. Значение vm fault minor является индикатором того, что ошибка обращения к странице была обработана без блокирования текущего процесса; этот тип ошибки называется незначительной ошибкой. Значение vm fault major свидетельствует о том, что ошибка привела к блокированию текущего процесса (вероятнее всего, потому что потребовалось определенное время на заполнение страничного кадра, выделенного процессу, данными, прочитанными с диска). Ошибка обращения к странице, приведшая к блокированию текущего процесса, называется значительной ошибкой. Кроме того, функция может возвратить значение vm fault oom (недостаточно памяти) или vm fault sigbus (любая другая ошибка).
Если функция handie_mm_fauit () возвращает значение vm_fault_sigbus, процессу отправляется сигнал sigbus:
if (ret == VM_FAULT_SIGBUS) { do_sigbus:
up_read (& ts k->mm->mmap_sem) ; if (!(error_code & 4)) / Kernel Mode / goto no_context; tsk->thread.cr2 = address; tsk->thread.error_code = error_code; tsk->thread.trap_no = 14;
info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRERR; info.si_addr = (void ) address; force_sig_infо(SIGBUS, Sinfo, tsk);}
Если функция handie mm fauit () не может выделить новый страничный кадр, она возвращает значение vm fault oom. В этом случае ядро обычно уничтожает текущий процесс. Однако, если текущим является процесс init, он ставится в конец очереди на выполнение, и вызывается планировщик. Когда процесс init возобновит работу, будет снова вызвана функция handle_
mm_fault():if (ret == VM_FAULT_OOM) { out_o f _memo г у:
up_read (&tsk->mm->mmap_sem) ; if (tsk->pid != 1) {
if (error_code & 4) / User Mode / do_exit(SIGKILL); goto no_context;}yield();
down_read (&tsk->mm->mmap_sem) ; goto survive;}
Функция handiemmf auit принимает четыре параметра:
- mm— указатель на дескриптор памяти процесса, выполнявшегося, когда возникло исключение;
- vma— указатель на дескриптор области памяти, содержащий линейный адрес, который вызвал исключение;
- address — линейный адрес, который вызвал исключение;
- write access — единица, если программа tsk пыталась произвести запись по адресу address, и ноль, если имела место попытка чтения или выполнения.
Функция начинается с проверки существования среднего каталога страниц и Таблицы Страниц, используемых для отображения адреса address. Даже если адрес принадлежит адресному пространству процесса, необходимые Таблицы Страниц могут быть еще не выделены, так что задача их выделения становится первоочередной:
pgd = pgd_offset(mm, address); spin_lock (&mm->page_table_lock) ; pud = pud_alloc(mm, pgd, address); if (pud) {
pmd = pmd_alloc(mm, pud, address); if (pmd) {
pte = pte_alloc_map(mm, pmd, address); if (pte)
return handle_pte_fault(mm, vma, address,
write_access, pte, pmd);}}
spin_unlock(&mm->page_table_lock); return VM_FAULT_OOM;
Локальная переменная pgd содержит запись глобального каталога страниц, ссылающуюся на адрес address. В случае необходимости вызываются функции pud aiiocO и pmd aiiocO для выделения нового верхнего каталога страниц и нового среднего каталога страниц соответственно .
Затем, опять же, при необходимости, вызывается функция pte aiioc mapO для выделения новой Таблицы Страниц. Если обе операции прошли успешно, локальная переменная pte указывает на запись в Таблице Страниц, ссылающуюся на адрес address. Далее вызывается функция handie pte fauit о для изучения записи Таблицы Страниц, соответствующей адресу address, и выяснения способа выделения нового страничного кадра процессу:
- если страница, к которой произошло обращение, отсутствует (то есть еще не находится ни в одном страничном кадре), ядро выделяет новый страничный кадр и инициализирует его, как полагается; этот способ называется выделением страниц по требованию;
- если страница, к которой произошло обращение, присутствует, но доступна только для чтения (то есть уже находится в каком-то страничном кадре), ядро выделяет новый страничный кадр и инициализирует его, копируя данные из старого страничного кадра; этот способ называется копированием при записи.
Предыдущая страница | 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 | Следующая страница