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

:: Меню ::

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

:: Друзі ::

Карта сайту
 

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

 

 

 

 

 

Секс с проституткой. | формы для живого организма установка очистки воды в квартире новые экологически чистые методы

Бібліотеки, що розділяються

Раніше ми згадували бібліотеки, що розділялися, як одна з переваг сторінкових і сегментних диспетчерів пам'яті перед базовими і банковими. При базовій адресації образ кожного процесу повинен займати безперервні області як у фізичному, так і в логічному адресному просторі. У цих умовах реалізувати бібліотеку, що розділяється, неможливо. Але і при використанні сторінкової адресації не все так просто.
Використання бібліотек, що розділяються, і DLL (в даному випадку різниця між ними не принципова) передбачає ту або іншу форму збірки у момент завантаження: виконуваний модуль має недозволені адресні заслання і імена бібліотек, які йому потрібні. При завантаженні ці бібліотеки підвантажуються і заслання вирішуються. Проблема тут в тому, що при Підвантаженні бібліотеки її потрібно перемістити, перенастроює абсолютні адресні заслання в її коді і даних (див. главу 3). Якщо в різних процесах бібліотека буде налаштована на різні адреси, вона вже не буде такою, що розділяється (мал. 5.14)! Якщо бібліотеки, що розділяються, можуть мати недозволені заслання на інші бібліотеки, проблема лише посилюється — до переміщуваних заслань додаються ще і зовнішні.

Мал. 5.14. Конфліктуючі адреси відображення DLL

У старих системах сімейства Unix, що використали абсолютні завантажувані модулі формату а.out, бібліотеки, що розділяються, також поставлялися у форматі абсолютних модулів, налаштованих на фіксовані адреси. Кожна бібліотека була налаштована на ЗБІЙ адреса. Постачальник нових бібліотек повинен був погоджувати цю адресу з розробниками системи. Це було вельми непрактично, бібліотек, що тому розділяються, було дуже мало (особливо якщо не рахувати ті, які входили в постачання ОС).
Прийнятніше вирішення цієї проблеми реалізоване в Os/2 2.x і Win32 (обидві ця архітектура є розвитком систем з єдиним адресним простором). Ідея полягає в тому, щоб виділити область адрес під завантаження DLL і відображувати цю область в адресні простори всіх процесів. Таким чином, всі DLL, завантажені в системі, видно всім (мал. 5.15).
Очевидним недоліком такого рішення (як, втім, і попереднього) є неефективне використання адресного простору: при скільки-небудь складній суміші завантажених програм більшої частини процесів більшість бібліотек буде просто не потрібні. В ті часи, коли ця архітектура розроблялася, це ще не видавалося серйозною трудністю, але зараз, коли багатьом додаткам стає тісно в 4 Гбайт і сервери з таким об'ємом оперативної пам'яті вже не рідкість, це дійсно може стати проблемою.

Мал. 5.15. Завантаження DLL в Os/2 і Win32

Менш очевидний, але серйозніший недолік полягає в тому, що ця архітектура не дозволяє двом застосуванням одночасно використовувати Дві різні, але однойменні DLL — наприклад, дві різні версії стандартної бібліотеки мови С. Поетому або ми вимушені вимагати від всіх бібліотек, що розділяються, абсолютної (bug-fbr-bug) сумісності версій, або чесно визнати, що далеко не кожна суміш прикладних програм буде працездатна. Перший варіант нереалістичний, другою ж створює значні незручності при експлуатації, особливо якщо система інтерактивна і розрахована на багато користувачів.
Позбавлене обох недоліків рішення пропонують сучасні системи сімейства Unix, що використовують завантажувані модулі формату ELF. Втім, для реалізації цього рішення довелося, ні багато, ні мало, переробити компілятор і навчити його генерувати не-залежний код (див. разд. Не-залежний код).

Бібліотеки формату ELF, що розділяються
Виконувані модулі формату ELF бувають двох типів: статичні — повністю самодостатні, не використовуючі об'єктів, що розділяються, і динамічні — такі, що містять заслання на об'єкти, що розділяються, і недозволені символи. І статичні, і динамічні модулі є абсолютними. При створенні образу процесу система починає з того, що відображує старечо зібраний виконуваний об'єкт в адресний простір. Статичний модуль не потребує ніякого додаткового налаштування і може почати виконання відразу після цього.
Для динамічного ж завантажувального модуля система завантажує так званий інтерпретатор, або редактор зв'язків часу виконання (run-time linker), no умовчанню ld.so.1. Він виконується в контексті процесу і здійснює підвантаження об'єктів, що розділяються, і скріплення їх з кодом основного модуля і другу другом.
При підвантаженні об'єкт, що розділяється, також відображується в адресний простір формованого процесу. Відображується він не на яку-небудь фіксовану адресу, а як вийде, з одним лише обмеженням: сегменти об'єкту будуть вирівняні на межу сторінки. Не гарантується навіть, що адреси сегментів будуть однакові при послідовних запусках однієї і тієї ж програми.
Документ [HOWTO Library] відверто стверджує, що в об'єктах, що розділяються, можна використовувати лише код, що компілює з ключем, -? роті. Документ [docs.sun.com 816-0559-10] менш категоричний:
"Якщо об'єкт, що розділяється, будується з коди, яка немає не-залежним, текстовий сегмент швидше за все зажадає велику кількість переміщень під час виконання. Хоча редактор зв'язків і здатний їх обробити, накладні витрати, що виникають внаслідок цього, можуть вести до серйозного зниження продуктивності".
Як вже говорилося в разд. 3.5, використовуваний в об'єктах, що розділяються, код немає істинно не-залежним: він містить переміщувані і такі, що навіть набудовуються адресні заслання, такі, як статично ініциалізованниє покажчики і заслання на процедури інших модулів. Але всі ці заслання розміщені в сегменті даних. Використовувані безпосередньо в коді заслання зібрані в дві таблиці, GOT (Global Offset Table, Глобальна таблиця зсувів) і PL Т (Procedure Linkage Table, Таблиця процедурного скріплення) (мал. 5.16). Кожен модуль, що розділяється, має свої власні таблиці. Породжений компілятором код визначає адреси цих таблиць, знаючи їх зсув в об'єкті, що розділяється, відносно точки входу функції (див. приклади 3.7 і 5.1)

Мал. 5.16. Global Offset Table (Глобальна таблиця зсувів) і Procedure Linkage Table (Таблиця процедурного скріплення)

Приклад 5.1. Типовий пролог функції, призначеної для використання в об'єкті, що розділяється

• text
•align 2,0x90
•globl _strerror
_strerror:
pushl %ebp ; Стандартний пролог функції
movl %esp,%ebp
pushl %ebx
call L4
popl %ebx ; Завантаження поточної адреси в регістр ЕВХ
acldl $_GLOBAL_OFFSET_TABLE_+ [ . -L4 ], %ebx

Сегмент коди відображується з розділенням його між всіма процесами, що використовують об'єкт (звичайно, за умови, що він компілював з правильними ключами і не містить переміщуваних адрес).
Навпаки, сегмент даних і таблиці GOT і PLT створюються в кожному образі заново і, якщо це необхідно, адресні заслання в них піддаються переміщенню. У міру дозволу зовнішніх заслань, інтерпретатор заповнює р|т об'єкту засланнями на символи, визначені в інших об'єктах (подібний стиль роботи із зовнішніми засланнями широко поширений в байт-кодах мов, що інтерпретуються, — див. разд. Збірка у момент звгрузки).
Сегмент даних об'єкту, що розділяється, таким чином, відповідає тому що в Os/2 і Win32 називається приватним сегментом даних DLL: кожне завдання, що використовує об'єкт, має свою копію цього сегменту. Аналога глобальному сегменту даних бібліотеки ELF, що розділяються, не мають — et%; це необхідно, код бібліотеки може створити власний сегмент пам'яті, що розділяється, але в нім неможливо мати статично ініциалізованниє дані і для нього ніхто не гарантує відображення на одні і ті ж адреси різних процесів, тому в нім неможливо зберігати покажчики.
За умовчанням, інтерпретатор здійснює відкладене редагування зв'язків: якщо сегмент даних він повністю набудовує до передачі управління призначеному для користувача коду, то записи в PLT спочатку вказують на спеціальну процедуру редактора зв'язків. Будучи викликана, ця процедура по стеку виклику або іншими засобами визначає, яку ж процедуру намагалися викликати насправді, і набудовує її запис в PLT (мал. 5.17). У разі, коли більшість програм не викликають велику частину функцій, як це часто і буває при використанні бібліотек, що розділяються, це дає певний виграш в продуктивності.



Мал. 5.17. Редактор зв'язків часу виконання

Приклад 5.2. Структура PLT для процесора SPARC (цитується по [docs.sun.com 816-0559-10])

Перші два (спеціальні) записи PLT до завантаження програми:
.PLT0:
un imp
unimp
unimp .PLTl:
unimp
unimp
unimp
Звичайні записи PLT до завантаження програми:
.PLT101:
sethi (.-.PLT0),%gl
ba,a .PLTO
пір .PLT102:
sethi (.-.PLT0),%gl
ba,a .PLTO
nop
...
Спеціальні записи PLT після завантаження програми:
.PLT0:
save %sp,-64,°osp
call runtime-linker
пір
.PLT1:
.word identification
unimp
unimp
...
Звичайні записи PLT після налаштування:
Plt101:
sethi (.-.PLT0),%g1
sethi %hi(name1),%g1
jmpl %g1+%lo(namel),%g0
Plt102:
sethi (.-.PLT0),%g1
sethi %hi (name2),%g1
jmpl %g1+%lo(name2),%g0

Таким чином, кожне призначене для користувача завдання, завантажене в Unix, має власний адресний простір з власною структурою (мал. 5.18). Деякі ділянки пам'яті в різних завдань можуть збігатися, і це дозволяє заощадити ресурси за рахунок їх розділення. Це істотно менш глибоке розділення, ніж те, що досягається в Windows, але, як ми бачили в разд. Динамічні бібліотеки глибше і примусове розділення коди чревате серйозними проблемами.

Мал. 5.18. Бібліотеки ELF, що розділяються

Об'єкти ELF, що розділяються, ідентифікуються по імені файлу. Виконуваний модуль може посилатися на файл як по простому імені (наприклад, libc.so.1), так і з вказівкою дорозі (/usr/lib/libc.so.1). При пошуку файлу по простому імені редактор зв'язків шукає його в каталогах, вказаних в змінній середи LD Library_path у записі RPATH заголовка модуля і, нарешті, в каталогах за умовчанням, перерахованих в конфігураційному файлі /var/ld/ld.config (саме у такому порядку [docs.sun.com 816-0559-10]). При формуванні імен каталогів можуть використовуватися макропідстановки з використанням наступних змінних.
$ISALIST— список систем команд— корисно на процесорах, що підтримують декілька систем команд, наприклад х86 і 8086, SPARC 32 і SPARC 64.
$ORIGIN — каталог, з якого завантажений модуль. Корисно для завантаження додатків, які мають власні об'єкти, що розділяються.
SOSNAME $OSREL — назва і версія операційної системи.
$PLATFORM— тип процесора. Корисно для додатків, які містять в постачанні бінарні модулі відразу для декількох процесорів, мережевих установок таких застосувань, або мережевого завантаження в гетерогенній середі.
Зрозуміло, що завдання простого імені переважно, оскільки дає адміністраторові системи значну свободу в розміщенні бібліотек, що розділяються. Втім, системний редактор зв'язків Idd дозволяє змінювати імена зовнішніх заслань і RPATH у вже побудованому модулі, зокрема замінюючи одні файлові дороги на інших, путні імена на простих і навпаки. Завдяки цьому, постачальник додатків для ОС, заснованих на форматі ELF, має значно менше можливостей зіпсувати життя системному адміністраторові, чим постачальник додатків для Windows.
За стандартною угодою, ім'я бібліотеки обов'язково містить і номер версії (у обох прикладах це 1). Відповідно до вимог фірми Sun номер версії міняється, лише коли інтерфейс бібліотеки міняється на несумісний — забираються функції, змінюється їх семантика і так далі З менш очевидних міркувань [docs.sun.com 816-0559-10] потрібно міняти номер версії і при додаванні функції або змінній: адже знов доданий символ може конфліктувати по імені з символом якийсь іншої бібліотеки.
Виправлення помилок, тобто порушення "Bug-for-bug compatibility", підставою для зміни номера версії фірма Sun не вважає. Навпаки, в Linux прийнято забезпечувати бібліотеки, що розділяються, мінімум двома, а інколи і більш номерами версій — старша (major) версія змінюється по правилах, приблизно відповідних вимогах Sun, а молодші (minor) — після виправлення окремих помилок і інших дрібних змін.
Завдяки цій угоді, в системі одночасно може бути встановлене декілька версій одного і того ж модуля, а призначені для користувача програми можуть посилатися саме на ту версію, з якою розроблялися і на сумісність з якою тестувалися. Адміністратор може управляти вибором саме тієї бібліотеки, на яку посилаються конкретні модулі, або змінюючи заслання в цих модулях за допомогою Idd, або використовуючи символічні зв'язки.

 

:: Реклама ::

Самоспасатель цена

 

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


 

 

 


Copyright © Kivik, 2017