Теорія операційної системи

:: Меню ::

Головна
Представлення даних в обчислювальних системах
Машинні мови
Завантаження програм
Управління оперативною пам'яттю
Сегментна і сторінкова віртуальна пам'ять
Комп'ютер і зовнішні події
Паралелізм з точки зору програміста
Реалізація багатозадачності на однопроцесорних комп'ютерах  
Зовнішні пристрої
Драйвери зовнішніх пристроїв
Файлові системи
Безпека
Огляд архітектури сучасних ОС

:: Друзі ::

Карта сайту
 

:: Статистика ::

 

 

 

 

 

Виключення

Багато процесорів використовують механізм, родинний перериванням, для обробки не лише зовнішніх, але і внутрішніх подій: ми з вами вже стикалися з винятковими ситуаціями (exception) відсутності сторінки і помилки доступу в процесорах з віртуальною пам'яттю, а також деякими іншими — помилкою шини при доступі до невирівняних слів, заповнення і очищення регістрового вікна в SPARC і так далі Більшість сучасних процесорів надають виключення при невідомому коді операції, діленні на нуль, арифметичному переповнюванні або, наприклад, виході значення операнда за допустимий діапазон в таких операціях, як обчислення логарифма, квадратного кореня або арксинуса.
Виняткові ситуації обробляються аналогічно зовнішнім перериванням: виконання програми зупиняється, і управління передається на процедуру-обработчик, адреса якої визначається природою виключення.
Відзнака полягає в тому, що переривання обробляються після завершення поточної команди, а повернення з обробника приводить до виконання команди, наступної за перерваною. Виключення ж приводить до припинення виконання поточної команди (якщо в процесі виконання команди ми вже встигли створити якісь побічні ефекти, вони відміняються), і збережений лічильник команд вказує на перервану інструкцію. Повернення з обробника, таким чином, приводить до спроби повторного виконання операції, що викликала виключення.
Завдяки цьому, наприклад, обробник сторінкової відмови може підкачати з диска вміст сторінки, що викликала відмову, перенастроювати таблицю дескрипторів і повторно виконати операцію, яка породила відмову. Обробник виключення за невизначеним кодом операції може використовуватися для емуляції розширень системи команд.
Наприклад, за наявності арифметичного співпроцесора операції з плаваючою крапкою виповнюються їм, а за відсутності — пакетом емулюючих підпрограм. Завдяки цьому може забезпечуватися повна бінарна сумісність між старшими (що мають співпроцесор) і молодшими (що не мають його) моделями одного сімейства комп'ютерів.
Виключення, що виникають при виконання привілейованих команд в призначеному для користувача режимі, можуть використовуватися системою віртуальних машин. Ядро ОС, що працює у віртуальній машині, вважає, що виконується в системному режимі. На самій же справі воно працює в призначеному для користувача режимі, а привілейовані команди (перемикання режиму процесора, налаштування диспетчера пам'яті, команди ввода/вивода) приводять до виклику СВМ.
При грамотній реалізації обробників таких виключень їх обробка Станеться повністю прозоро для тієї, що породила ці виключення програми. Звичайно, "підкочування" сторінки з диска або програмна емуляція плаваючого множення займе значно більше часу, чим просте звернення до пам'яті або апаратний реалізоване множення, але, напевно, Споживач обчислювальної системи знав, що робив, коли встановлював недостатню кількість пам'яті або набував машини без співпроцесора.
Багато інших виключень, таких, як ділення на нуль, зазвичай бессмь1с ленний обробляти повторною спробою ділення на якесь інше число В цьому випадку доцільно повернути управління не на команду, викликавши шую виключення, а в якусь іншу крапку. Питання, втім, в тому, куди саме слід повертатися. Зрозуміло, що код, який може восстано витися в разі ділення на нуль, сильно залежить від контексту, в якому сталася помилка (приклад 6.2).

Приклад 6.2. Обробка виключення Floating underflow (антіпереполніє при операціях з плаваючою крапкою)

#tinclude <setjmp.h>
static jmp_buf fpe_retry;
void fpe_handler (int sig) {
4> __fpreset () ; longjmp (fpe__retry, -1) ;
int compare_pgms (Image * imgo, Image * img1) {
int xsize=256, ysize=256;
int i, j, po, pi, pd;
double avg, avgsq, scale, smooth;
scale= (double) xsize* (double) ysize;
avg = 0.0; avgsq = 0.0;
/* Подавити можливі антипереповнення */
signal (SIGFPE, fpe_handler) ;
for(i=0; i<ysize; i smooth = (double) (imgo->picture [i*xsize] -imgl->picture [i*xsize] ) ; for(j= 0; j<xsize; j++){ po=imgo->picture [ j+i*xsize] ; pl=imgl->picture [ j+i*xsize] ; pd= (po-pl);
if (setjmp (fpe_retry) == 0) { smooth = smooth* (1 . 0-smooth_factor) + (double) pd*smooth_factor;
vq += smooth; avgsq += smooth*smooth;
eise
smooth=0 . 0 ;
if (Setjmp(fpe_retry) == 0)
Aspersion = avgsq/scale-avg*avg/ (scale*scale) ;
else dispersion = 0.0;
signal (SIGFPE, SIGJDFL) ;
}

При програмуванні на асемблері це може бути реалізовано простою підміною адреси повернення в стеку. Багато мов високого рівня реалізують ті або інші засоби для обробки виключень. Рівень цих засобів різний в різних мовах, починаючи від пари функцій setjmp і longjmp у З [Керніган-рітчи 2000] (приклад 6.3) і закінчуючи операторами try/catch і throw C++ [Страуструп 1999] і Java [Вебер 1999].

Приклад 6.3. Вихідний текст функцій set jmp/ longjmp.

/ setjmp. s (emx+gcc) — Copyright (c) 1990-1996 by Eberhard Mattes
# include <emx/asm386.h>
.globl _setjmp, _longjmp
.text ALIGN
# define J_ebx 0
# define J_esi 4
# define J_edi 8
#define J_esp 12
#define J_ebp 16
# define J_eip 20
# define J_xcp 24
/ Слова із зсувами 28.. 44 зарезервовані
/ int setjmp (jmp_buf here)
_setjmp:
PROFILE__NOFRAME
movl l*4(%esp) %edx /* here */
raovl %ebx, J_ebx(%edx)
movl %esi, J_esi(%edx)
movl ledi, J_edi(%edx)
movl %ebp, J_ebp(%edx)
movl %esp, J_esp(%edx)
movl 0*4(%esp) %eax /* Адреса повернення */
movl %eax, J_eip(%edx)
cmpb $0 __osmode /* Os/2? */
je If /* No -> skip */
fs
movl 0, leax /* handler Обробник виключень */
movl %eax, J_xcp(%edx) 1: xorl %eax, leax
EPILOGUE(setjmp)
ALIGN
/ void longjmp (jmp_buf there, int n)
_longjmp:
Profile_noframe
cmpb $0 __osmode /* Os/2? */
je 2f /* No -> skip */
movl 1*4(%esp) %eax /* there */
pushl J_xcp(%eax)
call ___unwind2 /* відновити обробники сигналів */
addl $4 %esp 2: movl l*4(%esp), ledx /* there */
movl 2*4(%esp), leax /* n */
testl %eax, leax
jne 3f
incl %eax
3: movl J_ebx(%edx) %ebx
movl J_esi(ledx), lesi
raovl J_edi(%edx) %edi
movl J EBP(%edx) %ebp
J_esp(%edx)
J_eip(%edx>, %edx
%edx, 0*4(%espj /* адреса повернення */
EPILOGUE(longjmp) /* well ... */

Виключення до ЯВИ часто дозволяють уникнути використання нелюба структурними програмістами оператора goto. У об'єктно-орієнтованих (ОО) мовах цей механізм грає ще важливішу роль: у большинінстве таких мов — це єдиний спосіб повідомити про невдачу при виконання конструктора об'єкту.
Поважно підкреслити, втім, що виключення в сенсі ЯВУ і апаратні виключення процесора — різні речі. У багатозадачній ОС призначена для користувача програма не має безпосереднього доступу до обробки переривань і виключень. ОС надає сервіс, що дозволяє програмістові реєструвати обробники для тих або інших подій, як відповідних апаратним виключенням, так і породжуваних самою операційною системою, але виклик таких обробників завжди здійснюється в два етапи: спочатку виконується обробник, зареєстрований ядром (приклад 6.4), а він, якщо визнає потрібним і можливим, перемикається в призначений для користувача контекст і викликає обробник, зареєстрований користувачем. Середа виконання ЯВУ, у свою чергу, може реалізувати і свої обробники між сервісом операційної системи і засобами, доступними програмістові.

Приклад 6.4. Обробник арифметичних виключень в ядрі Linux I

/*
* Iinux/arch/i386/traps.c *
* Copyright (С) 1991, 1992 Linus Torvalds *
* Підтримка Pentium III FXSR, SSE
* Gareth Hughes <gareth@valinux.com>, May 2000 */
void die(const char * str, struct pt_regs * regs, long err) I
console_verbose(); spin_lock_irq(&die_lock); Printk("%s: %041x\n", str, err & Oxffff}; show_registers(regs);
spin_unlock_irq(&die_lock);
do_exit (SIGSEGV) ;
static inline void die_if_kernel (const char * str, struct pt_regs * regs long err)
{
if ( ! (regs->eflags & Vm_mask) && ! (3 & regs->xcs) ) die (str, regs, err);
static inline unsigned long get_cr2 (void) { unsigned long address;
/* отримати адресу */
_ asm _ ("movl %%cr2, %0" : "=r" (address));
return address;
static void inline do_trap(int trapnr, int signr, char *str, int vm86
struct pt_regs * regs, long error_code, siginfo_t *info) { if (vm86 && regs->eflags & Vm_mask)
goto vm86_trap; if ( ! (regs->xcs & 3) ) goto kernel_trap;
trap_signal: {
struct task_struct *tsk = current; tsk->thread. error_code = error_code; tsk->thread. trap_no = trapnr; if (info)
force_sig_info (signr, info, tsk) ; else
force_sig (signr, tsk) ; return;
kernel_trap:
unsigned long fixup = search_exception_table(regs->eip); if (fixup)
regs->eip = fixup; else
die(str, regs, error_code); return;
vm86_trap: {
int ret = handle_vm86_trap((struct kernel_vm86_regs *) regs, er-ror_code, trapnr);
if (ret) goto trap_signal; return;
fldefine Do_error(trapnr, signr, str, name)\
asmlinkage void do_tt#name(struct pt_regs * regs, long error_code)\ { \ do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \
Idefine Do_error_info(trapnr, signr, str, name, sicode, siaddr)\ asmlinkage void do_t#name(struct pt_regs * regs, long error_code)\ { \
siginfo_t info; \
info.si_signo = signr; \
info.si_errno =0; \
info.si_code = sicode; \
info.si_addr = (void *)siaddr; \
do^trap(trapnr, signr, str, 0, regs, error_code, Sinfo); \ }
ttdefine Do_vm86_error(trapnr, signr, str, name)\
asmlinkage void do_##name(struct pt_regs * regs, long error_code)\ ( \
do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ }
ttdefine Do_vm86__ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
I
asmlinkage void do_##name(struct pt_regs * regs, long error_code)\ { \
siginfo_t info; \
info.si_signo = signr; \
info.si_errno =0; \
info.si_code = sicode; \
info.si_addr = (void *)siaddr; \
do_trap(trapnr, signr, str, 1, regs, error_code, sinfo); \
' "
Do_vm86_error_info( 0, SIGFPE, "Divide error", divide_error, Fpe_intdiv, regs->eip)
Do_vm86_error( 3, SIGTRAP, "int3", int3)
Do_vm86_error( 4, SIGSEGV, "overflow", overflow)
Do_vm86_error( 5, SIGSEGV, "bounds", bounds)
Do_error_info( 6, SIGILL, "Invalid operand", invalid_op, Ill_illopn, regs->eip)
Do_vm86_error( 7, SIGSEGV, "Device not available", device_not_available) Do_error( 8, SIGSEGV, "Double fault", double_fault)
Do_error( 9, SIGFPE, "Coprocessor segment overrun", coproces-sor_segment_overrun)
Do_error(10, SIGSEGV, "Invalid TSS", invalidjtssl
Do_error(11, SIGBUS, "Segment not present", segment_not_present)
Do_error(12, SIGBUS, "Stack segment", stack_segment)
Do_error_info(17, SIGBUS, "Alignment check", alignment_check, Bus_adraln
get_cr2 () )

 

:: Реклама ::

 

:: Посилання ::


 

 

 


Copyright © Kivik, 2017