Програмування в linux з нуля - Глава3

Глава 3. БІБЛІОТЕКИ

3.1. Введення в бібліотеки

Як вже неодноразово згадувалося в попередньому розділі, бібліотека - це набір скомпонованих особливим чином об'єктних файлів. Бібліотеки підключаються до основної програми під час компонування. За способом компонування бібліотеки поділяють на архіви (статичні бібліотеки, static libraries) і спільно використовуються (динамічні бібліотеки, shared libraries). У Linux, крім того, є механізми динамічної підвантаження бібліотек. Суть динамічної підвантаження полягає в тому, що запущена програма може на власний розсуд підключити до себе будь-яку бібліотеку. Завдяки цій можливості створюються програми з підключаються плагінами. такі як XMMS. У цьому розділі ми не будемо розглядати динамічну подгрузку, а зупинимося на класичному використанні статичних і динамічних бібліотек.

З точки зору моделі КІС, бібліотека - це сервер. Бібліотеки несуть в собі одну важливу думку: можливість використовувати одні і ті ж механізми в різних програмах. У Linux бібліотеки використовуються повсюдно, оскільки це дуже зручний спосіб "не вигадувати велосипеди". Навіть ядро ​​Linux в якомусь сенсі являє собою бібліотеку механізмів, які називаються системними викликами.

Статична бібліотека - це просто архів об'єктних файлів, який підключається до програми під час компонування. Ефект такий же, як якщо б ви підключали кожен з файлів окремо.

У Linux статичні бібліотеки зазвичай мають розширення .a (Archive), а спільно використовувані бібліотеки мають розширення .so (Shared Object). Зберігаються бібліотеки, як правило, в каталогах / lib і / usr / lib. У разі іншого розташування (відноситься тільки до спільно використовуваних бібліотек), доводиться трохи "подшаманить", щоб програма запустилася.

3.2. Приклад статичної бібліотеки

Тепер давайте створимо свою власну бібліотеку, яка має двома функціями: h_world () і g_world (), які виводять на екран "Hello World" і "Goodbye World" відповідно. Почнемо зі статичної бібліотеки.

Почнемо з інтерфейсу. Створимо файл world.h: Тут просто оголошені функції, які будуть використовуватися.

Тепер треба реалізувати сервери. Створимо файл h_world.c: Тепер створимо файл g_world.c, що містить реалізацію функції g_world (): Можна було б з таким же успіхом вмістити обидві функції в одному файлі (hello.c, наприклад), проте для наочності ми рознесли код на два файли .

Тепер створимо файл main.c. Це клієнт, який буде користуватися послугами сервера:

Тепер напишемо сценарій для make. Для цього створюємо Makefile: Не забувайте ставити табуляції перед кожним правилом в цільових зв'язках.

Залишилося тільки перевірити, чи працює програма і розібратися, що ж ми таке зробили:

Отже, в наведеному прикладі з'явилися три нові речі: опції -l і -L компілятора, а також команда ar. Почнемо з останньої. Як ви вже здогадалися, команда ar створює статичну бібліотеку (архів). У нашому випадку два об'єктних файлу об'єднуються в один файл libworld.a. У Linux практично всі бібліотеки мають префікс lib.

Як вже говорилося, компілятор gcc сам викликає лінковщік, коли це потрібно. Опція -l, передана компілятору, обробляється і надсилається лінковщік для того, щоб той підключив до бінарники бібліотеку. Як ви вже помітили, в ім'я бібліотеки "обрубані" префікс і суфікс. Це робиться для того, щоб створити "видиме байдужість" між статичними і динамічними бібліотеками. Але про це мова піде в інших розділах книги. Зараз важливо знати лише те, що і бібліотека libfoo.so і бібліотека libfoo.a підключаються до проекту опцією -lfoo. У нашому випадку libworld.a "урізати" до -lworld.

Опція -L вказує лінковщік, де йому шукати бібліотеку. У разі, якщо бібліотека розташовується в каталозі / lib або / usr / lib, то питання відпадає сам собою і опція -L не потрібно. У нашому випадку бібліотека знаходиться в репозиторії (в поточному каталозі). За замовчуванням лінковщік не проглядається поточний каталог в пошуку бібліотеки, тому опція -L. (Точка означає поточний каталог) необхідна.

3.3. Приклад спільно використовуваної бібліотеки

Для того, щоб створити і використовувати динамічну (спільно використовувану) бібліотеку, досить переробити в нашому проекті Makefile.

Зовні нічого не змінилося: програма компілюється, запускається і виконує ті ж самі дії, що і в попередньому випадку. Змінилася внутрішня суть. яка грає для програміста першочергову роль. Розглянемо все по порядку.

Правило для збірки binary тепер містить страшну опцію -Wl, -rpath ,. Нічого страшного тут немає. Як уже неодноразово говорилося, компілятор gcc сам викликає лінковщік ld, коли це треба і передає йому потрібні параметри збірки, позбавляючи нас від непотрібної переносних залежною тяганини. Але іноді ми все-таки повинні втрутитися в цей процес і передати лінковщік "свою" опцію. Для цього використовується опція компілятора -Wl, option, optargs. Розшифровую: передати лінковщік (-Wl) опцію option з аргументами optargs. У нашому випадку ми передаємо лінковщік опцію -rpath з аргументом. (Точка, поточний каталог). Виникає питання: що означає опція -rpath? Як вже говорилося, лінковщік шукає бібліотеки в певних місцях; зазвичай це каталоги / lib і / usr / lib, іноді / usr / local / lib. Опція -rpath просто додає до цього списку ще один каталог. У нашому випадку це поточний каталог. Без вказівки опції -rpath, лінковщік "мовчки" збере програму, але при запуску нас чекатиме сюрприз: програма не запуститься через відсутність бібліотеки. Спробуйте прибрати опцію -Wl, -rpath. з Makefile і пересоберіте проект. При спробі запуску програма binary завершиться з кодом повернення 127 (про коди повернення буде розказано в наступних розділах). Те ж саме станеться, якщо викликати програму з іншого каталогу. Поверніть назад -Wl, -rpath. пересоберіте проект, підніміться на рівень вище командою cd. і спробуйте запустити бінарник командою world / binary. Нічого не вийде, оскільки в новому поточному каталозі бібліотеки немає.

Є один спосіб не передавати лінковщік додаткових опцій за допомогою -Wl - це використання змінної оточення LD_LIBRARY_PATH. У наступних розділах ми будемо детально торкатися теми оточення (environment). Зараз лише скажу, що у кожного користувача є так зване оточення (environment) представляє собою набір пар ЗМІННА = ЗНАЧЕННЯ, використовуваних програмами. Щоб подивитися оточення, досить набрати команду env. Щоб додати в оточення змінну, досить набрати export ЗМІННА = ЗНАЧЕННЯ, а щоб видалити змінну з оточення, треба набрати export -n ЗМІННА. Будьте уважні: export - це внутреннаяя команда оболонки BASH; в інших оболонках (csh, ksh.) використовуються інші команди для роботи з оточенням. Мінлива оточення LD_LIBRARY_PATH містить список додаткових "місць", розділених двоеточіеямі, де лінковщіх повинен шукати бібліотеку.

Не дивлячись на наявність двох механізмів передачі інформації про нестандартному розташуванні бібліотек, краще поміщати бібліотеки в кінцевих проектах в / lib і в / usr / lib. Допускається розташування бібліотек в підкаталоги / usr / lib і в / usr / local / lib (із зазначенням -Wl, -rpath). Але змушувати кінцевого користувача встановлювати LD_LIBRARY_PATH майже завжди є поганим стилем програмування.

Наступна важлива деталь - це процес створення самої бібліотеки. Статичні бібліотеки створюються за допомогою архіватора ar, а спільно використовувані - за допомогою gcc з опцією -shared. В даному випадку gcc знову ж викликає лінковщік, але не для складання бінарники, а для створення динамічної бібліотеки.