Курсовая на тему Разработка статических и динамических библиотек на языке программирования СC в операционных
Работа добавлена на сайт bukvasha.net: 2015-07-01Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Содержание
Введение
1 Компиляция программ на языке C/C++
2 Компиляция нескольких файлов
3 Создание библиотек объектных файлов
3.1 Библиотеки объектных файлов
3.2 Создание статической библиотеки
3.3 Создание динамической библиотеки
3.4 Использование динамических библиотек
4 Создание динамических библиотек
4.1 Функции работы с динамическими библиотеками
4.2 Создание динамической библиотеки для решения системы линейных уравнений
Выводы
Список использованной литературы
Введение
Простые программы обычно состоят из одного исходного файла. Но если программы становится большой, ее рекомендуется их разбивать на куски, которые функционально ограничены и закончены. Так как процесс правки при большом исходном тексте становится сложным и поиск небольшой ошибки может повлечь за собой вынужденное "изучение" кода заново. Также большой файл программы увеличивает время компиляции.
Но если каждая функция будет содержаться в отдельном файле, то таких файлов может оказаться десятки или даже сотни. Управлять таким количеством файлов очень сложно. Для этого был придуман механизм создания библиотек объектных файлов.
Библиотека объектных файлов - это файл содержащий несколько объектных файлов, которые будут использоваться вместе в стадии линковки программы. Библиотека содержит символьный индекс, который состоит из названий функций и переменных и т.д., которые содержатся в библиотеке. Это позволяет ускорить процесс линковки программы, так как поиск функций и переменных в объектных файлах библиотеки происходит намного быстрее, чем поиск в наборе указанных объектных файлов.
Поэтому использование библиотеки позволяет компактно хранить все требуемые объектные файлы в одном месте, и при этом значительно повысить скорость компиляции.
В курсовой работе приведены примеры создания и использования статических и динамических библиотек. Д разработано несколько простых динамических библиотек (возведение числа в степень и решение системы линейных уравнений)
1 Компиляция программ на языке C/C++
Компилятор превращает код программы на "человеческом" языке в объектный код понятный компьютеру. Компиляторов под Linux существует много, практически для каждого распространенного языка. Большинство самых востребованных компиляторов входит в набор GNU Compiler Collection, известных под названием GCC (http://gcc.gnu.org/).
Изначально аббревиатура GCC имела смысл GNU C Compiler, но в апреле 1999 года сообщество GNU решило взять на себя более сложную миссию и начать создание компиляторов для новых языков с новыми методами оптимизации, поддержкой новых платформ, улучшенных runtime-библиотек и других изменений (http://gcc.gnu.org/gccmission.html). Поэтому сегодня коллекция содержит в себе компиляторы для языков C, C++, Objective C, Chill, Fortran, Ada и Java, как библиотеки для этих языков (libstdc++, libgcj, ...).
Компиляция программ производится командой:
gcc <имя_файла>
После этого, если процесс компиляции пройдет успешно, то вы получите загружаемый файл a.out, запустить который можно командой:
./a.out
Для примера давайте напишем маленькую простейшую программку:
#include <stdio.h>
int main(){
printf("[http://linux.firststeps.ru]\n");
printf("Our first program for Linux.\n");
return 0;
};
2 Компиляция нескольких файлов
Обычно простые программы состоят из одного исходного файла. Дело обстоит несколько сложнее, если эта программа становится большой. При работе с такой программой может возникнуть несколько достаточно серьезных проблем:
Файл, становясь большим, увеличивает время компиляции, и малейшие изменения в исходном тексте автоматически вынуждают тратить время программиста на перекомпиляцию программы.
Если над программой работает много человек, то практически невозможно отследить сделанные изменения.
Процесс правки и само ориентирование при большом исходном тексте становится сложным и поиск небольшой ошибки может повлечь за собой вынужденное "изучение" кода заново.
Это далеко не все пробемы, которые могут возникнуть при наличии программы "монстра". Поэтому при разработке программ рекомендуется их разбивать на куски, которые функционально ограничены и закончены. В этом значительно помогает сам язык C++, предоставляя свой богатый синтаксис.
Для того, чтобы вынести функцию или переменную в отдельный файл надо перед ней поставить зарезервированное слово extern. Для примера создадим программу из нескольких файлов. Сначала создадим главную программу, в которой будут две внешние процедуры. Назовем этот файл main.c:
#include <stdio.h>
// описываем функцию f1() как внешнюю
extern int f1();
// описываем функцию f2() как внешнюю
extern int f2();
int main()
{
int n1, n2;
n1 = f1();
n2 = f2();
printf("f1() = %d\n",n1);
printf("f2() = %d\n",n2);
return 0;
}
Теперь создаем два файла, каждый из которых будет содержать полное определение внешней функции из главной программы. Файлы назовем f1.c и f2.c:
// файл f1.c
int f1()
{
return 2;
}
// файл f2.c
int f2()
{
return 10;
}
После этого процесс компиляции программы с помощью gcc будет выглядеть несколько иначе от описанного в первой главе "Компиляция программ на языке C/C++".
Компилировать можно все файлы одновременно одной командой, перечисляя составные файлы через пробел после ключа -c:
gcc -c main.c f1.c f2.c
Или каждый файл в отдельности:
gcc -c f1.c
gcc -c f2.c
gcc -c main.c
В результате работы компилятора мы получим три отдельных объектных файла:
main.o
f1.o
f2.o
Чтобы их собрать в один файл с помощью gcc надо использовать ключ -o, при этом линкер соберет все файлы в один:
gcc main.o f1.o f2.o -o rezult
В результате вызова полученной программы rezult командой:
./rezult
На экране появится результат работы:
olya:~# ./rezult
f1() = 2
f2() = 10
olya:~#
Теперь, если мы изменим какую-то из процедур, например f1():
int f1()
{
return 25;
}
То компилировать заново все файлы не придется, а понадобится лишь скомпилировать измененный файл и собрать результирующий файл из кусков:
olya:~# gcc -c f1.c
olya:~# gcc main.o f1.o f2.o -o rezult2
olya:~# ./rezult2
f1() = 25
f2() = 10
olya:~#
Таким образом можно создавать большие проекты, которые больше не будут отнимать много времени на компиляцию и поиск ошибок. Однако нужно помнить, что не стоит также черезчур разбивать программу, иначе у нас получится несколько десятков файлов, в которых рано или поздно можно запутаться. Можно найти "золотую середину", например в отдельные файлы помещать те функции или классы, с которыми нам приходится больше всего работать при отладке. После того, как функция будет окончательно отлажена, ее вполне можно перенести в более крупный файл.
3 Создание библиотек объектных файлов
3.1 Библиотеки объектных файлов
В прошлом разделе курсовой работы мы создавали объектные файлы. Естественно, если каждая функция будет содержаться в отдельном файле, то таких файлов может оказаться десятки или даже сотни. Управлять таким количеством файлов очень сложно. Для этого был придуман механизм создания библиотек объектных файлов.
Библиотека объектных файлов - это файл содержащий несколько объектных файлов, которые будут использоваться вместе в стадии линковки программы. Нормальная библиотека содержит символьный индекс, который состоит из названий функций и переменных и т.д., которые содержатся в библиотеке. Это позволяет ускорить процесс линковки программы, так как поиск функций и переменных в объектных файлах библиотеки происходит намного быстрее, чем поиск в наборе указанных объектных файлов. Поэтому использование библиотеки позволяет компактно хранить все требуемые объектные файлы в одном месте, и при этом значительно повысить скорость компиляции.
Объектные библиотеки по способу использования разделяются на два вида:
Статические библиотеки
Динамические библиотеки
Статическая библиотека - это коллекция объектных файлов, которые присоединяются к программе во время линковки программы. Таким образом статические библиотеки используются только при созданиии программы. Потом в работе самой программы они не принимают участие, в отличие от динамических библиотек.
Динамическая библиотека - это созданная специальным образом библиотека, которая присоединяется к результирующей программе в два этапа. Первый этап, это естественно этап компиляции. На этом этапе линковщик встраивает в программу описания требуемых функций и переменных, которые присутствуют в библиотеке. Сами объектные файлы из библиотеки не присоединяются к программе. Присоединение этих объектных файлов(кодов функций) осуществляет системный динамический загрузчик во время запуска программы. Загрузчик проверяет все библиотеки прилинкованные с программе на наличие требуемых объектных файлов, затем загружает их в память и присоединяет их в копии запущенной программы, находящейся в памяти.
Сложный процесс загрузки динамических библиотек замедляет запуск программы, но у него есть существунный, даже можно сказать неоценимый плюс - если другая запускаемая программа линкована с этой же загруженной динамической библиотекой, то она использует туже копию библиотеки. Это означает, что требуется гораздо меньше памяти для запуска нескольких программ, сами загрузочные файлы меньше по размеру, что экономит место на дисках.
Однако если Вы модифицируете динамическую библиотеку и попытаетесь ее использовать при запуске программы, то если загрузчик обнаружит уже загруженную старую библиотеку он будет упорно использовать ее функции. При этом Вы так и не сможете загрузить новую версию библиотеки. Но есть пути решения этой проблемы, и их мы рассмотрим позже.
3.2 Создание статической библиотеки
Для создания статических библиотек существует специальная простая программа называемая ar (сокр. от archiver - архиватор). Она используется для создания, модификации и просмотра объектных файлов в статических библиотеках, которые в действительности представляют из себя простые архивы.
Создадим из файлов f1.c и f2.c отдельную библиотеку.
// файл f1.c
int f1()
{
return 2;
}
// файл f2.c
int f2()
{
return 10;
}
Для начала компилируем эти файлы:
olya:~# gcc -c f1.c f2.c
В результате получим, как обычно, два файла - f1.o и f2.o. Для того, чтобы создать библиотеку из объектых файлов надо вызвать программу ar со следующими параметрами:
ar rc libимя_библиотеки.a [список_*.o_файлов]
Допустим наша библиотека будет называться fs, тогда команда запишется в виде:
olya:~# ar rc libfs.a f1.o f2.o
В результате получим файл libfs.a, в котором будут лежать копии объектых файлов f1.o и f2.o. Если файл библиотеки уже существует, то архиватор будет анализировать содержимое архива, он добавит новые объектные файлы и заменит старые обновленными версиями. Опция c заставляет создавать (от create) библиотеку, если ее нет, а опция r (от replace) заменяет старые объектные файлы новыми версиями.
Пока у нас есть лишь архивный файл libfs.a. Чтобы из него сделать полноценную библиотеку объектных файлов надо добавить к этому архиву индекс символов, т.е. список вложенных в библиотеку функций и переменных, чтобы линковка происходила быстрее. Далается это командой:
ranlib libимя_библиотеки.a
Программа ranlib добавит индекс к архиву и получится полноценная статическая библиотека объектных файлов. Стоит отметить, что на некоторых системах программа ar автоматически создает индекс, и использование ranlib не имеет никакого эффекта. Но тут надо быть осторожным при атоматической компиляции библиотеки с помощью файлов makefile, если не использовать утилиту ranlib, то возможно на каких-то системах библиотеки будут создаваться не верно и потеряется независимость от платформы. Так что возьмем за правило тот факт, что утилиту ranlib надо запускать в любом случае, даже если он нее нет никакого эффекта.
Для компиляции нашего основного файла main.c надо сообщить компилятору, что надо использовать библиотеки. Чтобы компилятор знал где искать библиотеки ему надо сообщить каталог, в котором они содержатся и список этих билиотек. Каталог с библиотеками указывается ключом -L, в нашем случае библиотека находится в текущем каталоге, значит путь до нее будет в виде точки (-L.). Используемые библиотеки перечисляются через ключ -l, после которого указывается название библиотеки без префикса lib и окончания .a. В нашем случае этот ключ будет выглядеть, как -lfs. Теперь все одной командой:
olya:~# gcc -c main.c
olya:~# gcc main.o -L. -lfs -o rezult
Или можно чуть короче:
olya:~# gcc main.c -L. -lfs -o rezult
Заметьте, что компилятору нужны библиотеки на этапе создания конечного файла, т.е. линковки. В первом случае процесс компиляции совершается первой командой, а сборка файла второй командой. Если же мы попытаемся подсунуть библиотеку на этапе компиляции, то получим вежливый ответ:
olya:~# gcc -c main.c -L. -lfs
gcc: -lfs: linker input file unused since linking not done
Что означает, что файлы библиотек не нужны, до процесса линковки. Данная команда создаст лишь файл main.o, который в итоге потом придется собирать отдельно.
3. 3 Создание динамической библиотеки
Как мы уже говорилось в курсовой работе, динамические библиотеки немного лучше статических, но их использование более сложное. И не из-за того, что процесс загрузки программы замедляется. Проблемы начинаются уже на этапе компиляции.
Для начала стоит сказать, что объектный файл создаваемый нашим проверенным способом вовсе не подходит для динамических библиотек. Связано это с тем, что все объектные файлы создаваемые обычным образом не имеют представления о том в какие адреса памяти будет загружена использующая их программа. Несколько различных программ могут использовать одну библиотеку, и каждая из них располагается в различном адресном пространстве. Поэтому требуется, чтобы переходы в функциях библиотеки (операции goto на ассемблере) использовали не абсолютную адресацию, а относительную. То есть генерируемый компилятором код должен быть независимым от адресов, такая технология получила название PIC - Position Independent Code. В компиляторе gcc данная возможность включается ключом -fPIC.
Теперь компилирование наших файлов будет иметь вид:
olya:~# gcc -fPIC -c f1.c
olya:~# gcc -fPIC -c f2.c
Динамическая библиотека это уже не архивный файл, а настоящая загружаемая программа, поэтому созданием динамических библиотек занимается сам компилятор gcc. Для того, чтобы создать динамическую библиотеку надо использовать ключ -shared:
olya:~# gcc -shared -o libfsdyn.so f1.o f2.o
В результате получим динамическую библиотеку libfsdyn.so, которая по моей задумке будет динамической версией библиотеки libfs.a, что видно из названия :) Теперь, чтобы компилировать результирующий файл с использованием динамической библиотеки нам надо собрать файл командой:
olya:~# gcc -с main.с
olya:~# gcc main.o -L. -lfsdyn -o rezultdyn
Если теперь Вы сравните файлы, полученные при использовании статической и динамической библиотеки, то увидите, что их размеры отличаются. В данном случае файл созданный с динамической библиотекой занимает чуть больше места, но это лишь от того, что программа используемая нами совершенно примитивная и львиную долю там занимает специальный код для использования динамических возможностей. В реальных условиях, когда используются очень большие функции размер программы с использованием динамической библиотеки значительно меньше.
На этом фокусы не кончаются, если Вы сейчас попробуете запустить файл rezultdyn, то получите ошибку:
olya:~# ./rezultdyn
./rezultdyn: error in loading shared libraries: libfsdyn.so: cannot open
shared object file: No such file or directory
olya:~#
Это сообщение выдает динамический линковщик. Он просто не может найти файл нашей динамической библиотеки. Дело в том, что загрузчик ищет файлы динамических библиотек в известных ему директориях, а наша директория ему не известна. Но это мы чуть отложим, потому что это достаточно сложный вопрос.
А сейчас стоит поговорить еще об одном моменте использования библиотек. Мы специально динамическую библиотеку с названием fsdyn, чтобы она отличалась от названия статической библиотеки fs. Дело в том, что если у Вас две библиотеки статическая и динамическая с одинаковыми названиями, то есть libfs.a и libfs.so, то компилятор всегда будет использовать динамическую библиотеку.
Связано это с тем, что в ключе -l задается часть имени библиотеки, а префикс lib и окончание .a или .so приставляет сам компилятор. Так вот алгоритм работы компилятора таков, что если есть динамическая библиотека, то она используется по умолчанию. Статическая же библиотека используется когда компилятор не может обнаружить файл .so этой библиотеки. Во всей имеющейся у меня документации пишется, что если использовать ключ -static, то можно насильно заставить компилятор использовать статическую библиотеку. Отлично, попробуем...
olya:~# gcc -staticmain.o -L. -lfs -o rez1
Результирующий файл rez1 получается размером в 900 Кб. После применения программы strip размер ее уменьшается до 200 Кб, но это же не сравнить с тем, что наша первая статическая компиляция давала программу размером 10 Кб. А связано это с тем, что любая программа написанная на C/C++ в Linux использует стандартную библиотеку "C" library, которая содержит в себе определения таких функций, как printf(), write() и всех остальных. Эта библиотека линкуется к файлу как динамическая, чтобы все программы написанные на C++ могли использовать единожды загруженные функции. Ну, а при указании ключа -static компилятор делает линковку libc статической, поэтому размер кода увеличивается на все 200 Кб.
3.4 Использование динамических библиотек
В прошлый раз мы с Вами обнаружили, что запуск программ, скомпилированных вместе с динамическими библиотеками, вызывает ошибку:
olya:~# ./rezultdyn
./rezultdyn: error in loading shared libraries: libfsdyn.so: cannot open
shared object file: No such file or directoryolya:/#
Это сообщение выдает загрузчик динамических библиотек(динамический линковщик - dynamic linker), который в нашем случае не может обнаружить библиотеку libfsdyn.so. Для настройки динамического линковщика существует ряд программ.
Первая программа называется ldd. Она выдает на экран список динамических библиотек используемых в программе и их местоположение. В качестве параметра ей сообщается название обследуемой программы. Давайте попробуем использовать ее для нашей программы rezultdyn:
olya:~# ldd rezultdyn
libfsdyn.so => not found
libc.so.6 => /lib/libc.so.6 (0x40016000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
olya:~#
Как видите все правильно. Программа использует три библиотеки:
libc.so.6 - стандартную библиотеку функций языка C++.
ld-linux.so.2 - библиотеку динамической линковки программ ELF формата.
libfsdyn.so - нашу динамическую библиотеку функций.
Нашу библиотеку она найти не может. И правильно! Динамический линковщик ищет библиотеки только в известных ему каталогах, а каталог нашей программы ему явно не известен.
Для того, чтобы добавить нашу директорию с библиотекой в список известных директорий надо подредактировать файл /etc/ld.so.conf. Например, у меня этот файл состоит из таких строк:
olya:~# cat /etc/ld.so.conf
/usr/X11R6/lib
/usr/i386-slackware-linux/lib
/usr/i386-slackware-linux-gnulibc1/lib
/usr/i386-slackware-linux-gnuaout/lib
olya:~#
Во всех этих директории хранятся всеми используемые библиотеки. В этом списке нет лишь одной директории - /lib, которая сама по себе не нуждается в описании, так как она является главной. Получается, что наша библиотека станет "заметной", если поместить ее в один их этих каталогов, либо отдельно описать в отдельном каталоге. Давайте для теста опишем, добавим строку в конец файла ld.so.conf:
/root
У меня этот файл находится в домашнем каталога пользователя root, у Вас он может быть в другом месте. Теперь после этого динамический линковщик будет знать где можно найти наш файл, но после изменения конфигурационного файла ld.so.conf необходимо, чтобы система перечитала настройки заново. Это делает программа ldconfig. Пробуем запустить нашу программу:
olya:~# ldconfig
olya:~# ./rezultdyn
f1() = 25
f2() = 10
olya:~#
Как видите все заработало. Если теперь удалить добавленную нами строку и снова запустите ldconfig, то данные о расположении нашей библиотеки исчезнут и будет появляться таже самая ошибка.
Но описанный метод влияет на всю систему в целом и требует доступа администратора системы, т.е. root. А если Вы простой пользователь без сверх возможностей ?!
Для такого случая есть другое безболезненное решение. Это использование специальной переменной среды LD_LIBRARY_PATH, в которой перечисляются все каталоги содержащие пользовательские динамические библиотеки. Для того, чтобы установить эту переменную в командной среде bash надо набрать всего несколько команд. Для начала посмотрим есть ли у нас такая переменная среды:
olya:~# echo $LD_LIBRARY_PATH
У меня в ответ выводится пустая строка, означающая, что такой переменной среды нет. Устанавливается она следующим образом:
olya:~# LD_LIBRARY_PATH=/root
olya:~# export LD_LIBRARY_PATH
После этого программа rezultdyn будет прекрасно работать. В случае, если в системе эта переменная среды уже уставновлена, то, чтобы не испортить ее значение, надо новый каталог прибавить к старому значению. Делается это другой командой:
olya:~# LD_LIBRARY_PATH=/root:${LD_LIBRARY_PATH}
olya:~# export LD_LIBRARY_PATH
Если обнулить эту переменную, то снова библиотека перестанет работать:
olya:~# LD_LIBRARY_PATH=""
olya:~# export LD_LIBRARY_PATH
olya:~# ./rezultdyn
./rezultdyn: error in loading shared libraries: libfsdyn.so: cannot open
shared object file: No such file or directory
olya:~#
Также параллельно можно зайти в систему под другим пользователем или даже тем же самым, но если просмотреть значение LD_LIBRARY_PATH, то увидим ее прежнее значение. Это означает, что два разных пользователя Linux не могут влиять на работу друг друга, а это и есть самое главное хорошее отличие систем Unix от большинства других систем.
4 Создание динамических библиотек
4.1 Функции работы с динамическими библиотеками
Оказывается, что использовать динамические библиотеки можно не только в начале загрузки, но и в процессе самой работы программы. Программа сама может вызывать любые функции из библиотеки, когда ей захочется. Для этого всего-лишь надо использовать библиотеку dl, которая позволяет линковать библиотеки "на лету". Она управляет загрузкой динамических библиотек, вызовом функций из них и выгрузкой после конца работы.
Для использования функций программной работы с динамическими библиотеками необходимо подключить заголовочный файл:
#include <dlfcn.h>
Чтобы вызывать какие-то функции из динамической библиотеки сначала надо открыть эту библиотеку (можно сказать "загрузить"). Открывается она функцией:
void *dlopen (const char *filename, int flag);
Параметр filename содержит путь до требуемой библиотеки, а параметр flag задает некоторые специфические флаги для работы. Функция возвращает указатель на загруженную библиотеку. В случае любой ошибки возвращается указатель NULL. В таком случае тест ошибки понятный человеку можно получить с помощью функции dlerror(). Пока мы не будем задумываться над этим, и я приведу стандартный код для открытия библиотеки:
void *library_handler;
//......
//загрузка библиотеки
library_handler = dlopen("/path/to/the/library.so",RTLD_LAZY);
if (!library_handler){
//если ошибка, то вывести ее на экран
fprintf(stderr,"dlopen() error: %s\n", dlerror());
exit(1); // в случае ошибки можно, например, закончить работу программы
};
После этого можно работать с библиотекой. А работа эта заключается в получении адреса требуемой функции из библиотеки. Получить адрес функции или переменной можно по ее имени с помощью функции:
void *dlsym(void *handle, char *symbol);
Для этой функции требуется адрес загруженной библиотеки handle, полученный при открытии функцией dlopen(). Требуемая функция или переменная задается своим именем в переменной symbol.
Закрывается библиотека функцией:
dlclose(void *handle);
При закрытии библиотеки динамический линковщик проверяет счетчик количества открытий библиотеки, и если она была открыта несколькими программами одновременно, то она не выгружается до тех пор, пока все программы не закроют эту библиотеку.
Для примера создадим программу, которая в качестве параметра получает название функции, которую она будет использовать в работе. Например, это будут математические функции возведения в степень. Создадим сначала динамическую библиотеку. Пишем ее код:
double power2(double x){
return x*x;
};
double power3(double x){
return x*x*x;
};
double power4(double x){
return power2(x)*power2(x);
};
//......
Сохраняем его в файл lib.c и создаем динамическую библиотеку libpowers.so следующими командами:
olya:~# gcc -fPIC -c lib.c
olya:~# gcc -shared lib.o -o libpowers.so
Теперь создаем основную программу в файле main.c:
#include <stdio.h>
/* заголовочный файл для работы с динамическими библиотеками */
#include <dlfcn.h>
int main(int argc, char* argv[]){
void *ext_library;// хандлер внешней библиотеки
double value=0;// значение для теста
double (*powerfunc)(double x);// переменная для хранения адреса функции
//загрузка библиотеки
ext_library = dlopen("/root/libpowers.so",RTLD_LAZY);
if (!ext_library){
//если ошибка, то вывести ее на экран
fprintf(stderr,"dlopen() error: %s\n", dlerror());
return 1;
};
//загружаем из библиотеки требуемую процедуру
powerfunc = dlsym(ext_library, argv[1]);
value=3.0;
//выводим результат работы процедуры
printf("%s(%f) = %f\n",argv[1],value,(*powerfunc)(value));
//закрываем библиотеку
dlclose(ext_library);
};
Код главной программы готов. Требуется его откомпилировать с использованием библиотеки dl:
olya:~# gcc main.c -o main -ldl
Получим программный файл main, который можно тестировать. Наша программа должна возводить значение 3.0 в требуемую нами степень, которая задается названием функции. Давайте попробуем:
olya:~# ./main power2
power2(3.000000) = 9.000000
olya:~# ./main power3
power3(3.000000) = 27.000000
olya:~# ./main power4
power4(3.000000) = 81.000000
olya:~#
Мы используем функции, зная лишь их название. Представьте открывающиеся возможности для программ, на основе этого метода можно создавать плагины для программ, модернизировать какие-то части, добавлять новые возможности и многое другое.
4.2 Создание динамической библиотеки для решения системы линейных уравнений
В качестве примера использования динамических библиотек напишем программу для решения системы линейных уравнений.
Пусть система имеет вид:
a11*x1+a12*x2=b1;
a21*x1+a22*x2=b2;
Решение этой системы находим через обратную матрицу A-1.
A*X=B
X=A-1*B
; ; ;
;
Программа в качестве параметров принимает значение коефициентов системы и название функции, которую она использует.
Создадим динамическую библиотеку:
Так как функция возвращает два результата x1 и x2 в программе будем использовать структуру.
"mytype.h" :
struct DPair {
double first;
double second;
};
Struct DPair (*powerfunc)(double,double,double,double,double,double);
"libsysur.c" :
#include "mytype.h"
struct DPair sys2(double a11, double a12,double a21, double a22,double b1, double b2){
//nahodim opredelitel sistemy
struct DPair dPair;
double opr=a11*a22-a12*a21;
if (opr!=0) {
double ao11=a22; double ao12=-a21;
double ao21=-a12; double ao22=a11;
dPair.first=(ao11*b1+ao21*b2)/opr;
dPair.second=(ao12*b1+ao22*b2)/opr;
}
return dPair;
}
Создаем динамическую библиотеку libsysur.so следующими командами:
bash-3.00# gcc -fPIC -c libsysur.c
bash-3.00# gcc -shared libsysur.o -o libsysur.so
Создаем основную программу:
"sysur.c" :
#include <stdio.h>
#include <dlfcn.h>
#include "mytype.h"
int main(int argc, char* argv[]){
void *ext_library; //хандлер внешней функции
double a11;double a12; double a21; double a22;
double b1; double b2;
double (*powerfunc)(double a11,double a12, double a21, double a22,double b1, double b2);//переменная, для хранения адреса функции
//загрузка библиотеки
ext_library=dlopen("/root/dynamik/libsysur.so",RTLD_LAZY);
if (!ext_library){
//если ошибка, вывести на экран
fprintf(stderr,"dlopen() error: %s\n", dlerror());
return 1;
};
//загружаем из библиотеки требуемую процедуру
powerfunc=dlsym(ext_library, argv[1]); //получаем адрес требуемой функции
//ext_library-адрес загруженной библ.
//argv-требуемая функция или переменная
printf("Vvedite a11 a12 :");
scanf("%lf %lf",&a11,&a12);
printf("Vvedite a21 a22 :");
scanf("%lf %lf",&a21,&a22);
printf("Vvedite b1, b2 :");
scanf("%lf %lf",&b1,&b2);
struct DPair sq=(*powerfunc)(a11,a12,a21,a22,b1,b2);
printf("x1=%lf\n, x2=%d\lf",sq.first,sq.second);
//выводим результат работы процедуры
//закрываем библиотеку
dlclose(ext_library);
};
Компилируем код главной программы:
bash -3.00# gcc sysur.c -o sysur -ldl
Выводы
В курсовой работе были рассмотрены рассмотрены вопросы разработки статических и динамических библиотек на языке программирования С/C++ в операционных системах UNIX.
Использование библиотеки позволяет компактно хранить все требуемые объектные файлы в одном месте, и при этом значительно повысить скорость компиляции.
Объектные библиотеки по способу использования разделяются на статические и динамические библиотеки
Статическая библиотека - это коллекция объектных файлов, которые присоединяются к программе во время линковки программы. В работе самой программы статические библиотеки они не принимают участие, в отличие от динамических библиотек.
В динамической библиотеке объектные файлы из библиотеки не присоединяются к программе. Присоединение этих объектных файлов осуществляет системный динамический загрузчик во время запуска программы. Загрузчик проверяет все библиотеки прилинкованные с программе на наличие требуемых объектных файлов, затем загружает их в память и присоединяет их в копии запущенной программы, находящейся в памяти.
Динамические библиотеки немного лучше статических, но их использование более сложное. Так как динамическая библиотека это уже не архивный файл, а настоящая загружаемая программа
Список использований литературы
http://club.shelek.com/
http://helpsite.narod.ru/
http://myforum.net.ua/index.php?
http://ishodniki.ru/
Робин Бурк и др. Unix для системных администраторов.Энциклопедия пользователя. - Киев 1998. - 864с.