ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 20.06.2020

Просмотров: 397

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

dron:~# gcc -с main.с

dron:~# gcc main.o -L. -lfsdyn -o rezultdyn

Если теперь Вы сравните файлы, полученные при использовании статической и динамической библиотеки, то увидите, что их размеры отличаются. В данном случае файл созданный с динамической библиотекой занимает чуть больше места, но это лишь от того, что программа используемая нами совершенно примитивная и львиную долю там занимает специальный код для использования динамических возможностей. В реальных условиях, когда используются очень большие функции размер программы с использованием динамической библиотеки значительно меньше.

Если Вы сейчас попробуете запустить файл rezultdyn, то получите ошибку:

dron:~# ./rezultdyn

./rezultdyn: error in loading shared libraries: libfsdyn.so: cannot open

shared object file: No such file or directory

dron:~#

Это сообщение выдает загрузчик динамических библиотек (динамический линковщик - dynamic linker), который в нашем случае не может обнаружить библиотеку libfsdyn.so. Для настройки динамического линковщика существует ряд программ.

Первая программа называется ldd. Она выдает на экран список динамических библиотек используемых в программе и их местоположение. В качестве параметра ей сообщается название обследуемой программы. Давайте попробуем использовать ее для нашей программы rezultdyn:

dron:~# 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)

dron:~#

Как видите все правильно. Программа использует три библиотеки:

  • libc.so.6 - стандартную библиотеку функций языка C++.

  • ld-linux.so.2 - библиотеку динамической линковки программ ELF формата.

  • libfsdyn.so - нашу динамическую библиотеку функций.

Нашу библиотеку она найти не может. Динамический линковщик ищет библиотеки только в известных ему каталогах, а каталог нашей программы ему явно не известен.

Для того, чтобы добавить нашу директорию с библиотекой в список известных директорий надо подредактировать файл /etc/ld.so.conf. Например, пусть этот файл состоит из таких строк:

dron:~# 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

dron:~#

Во всех этих директории хранятся всеми используемые библиотеки. В этом списке нет лишь одной директории - /lib, которая сама по себе не нуждается в описании, так как она является главной. Получается, что наша библиотека станет "заметной", если поместить ее в один их этих каталогов, либо отдельно описать в отдельном каталоге. Давайте для теста опишем, добавим строку в конец файла ld.so.conf:

/root

Для примера, этот файл находится в домашнем каталога пользователя root, у Вас он может быть в другом месте. Теперь после этого динамический линковщик будет знать где можно найти наш файл, но после изменения конфигурационного файла ld.so.conf необходимо, чтобы система перечитала настройки заново. Это делает программа ldconfig. Пробуем запустить нашу программу:

dron:~# ldconfig

dron:~# ./rezultdyn

f1() = 25

f2() = 10

dron:~#

Как видите все заработало :) Если теперь Вы удалите добавленную нами строку и снова запустите ldconfig, то данные о расположении нашей библиотеки исчезнут и будет появляться таже самая ошибка.


Но описанный метод влияет на всю систему в целом и требует доступа администратора системы, т.е. root. А если Вы простой пользователь без сверх возможностей ?!

Для такого случая есть другое решение. Это использование специальной переменной среды LD_LIBRARY_PATH, в которой перечисляются все каталоги содержащие пользовательские динамические библиотеки. Для того, чтобы установить эту переменную в командной среде bash надо набрать всего несколько команд. Для начала посмотрим есть ли у нас такая переменная среды:

dron:~# echo $LD_LIBRARY_PATH

Если в ответ выводится пустая строка, то эт означает, что такой переменной среды нет. Устанавливается она следующим образом:

dron:~# LD_LIBRARY_PATH=/root

dron:~# export LD_LIBRARY_PATH

После этого программа rezultdyn будет прекрасно работать. В случае, если у Вас в системе эта переменная среды уже установлена, то, чтобы не испортить ее значение, надо новый каталог прибавить к старому значению. Делается это другой командой:

dron:~# LD_LIBRARY_PATH=/root:${LD_LIBRARY_PATH}

dron:~# export LD_LIBRARY_PATH

Если Вы обнулите эту переменную, то снова библиотека перестанет работать:

dron:~# LD_LIBRARY_PATH=""

dron:~# export LD_LIBRARY_PATH

dron:~# ./rezultdyn

./rezultdyn: error in loading shared libraries: libfsdyn.so: cannot open

shared object file: No such file or directory

dron:~#

Вы также параллельно можете зайти в систему под другим пользователем или даже тем же самым, но если Вы захотите просмотреть значение LD_LIBRARY_PATH, то увидите ее прежнее значение. Это означает, что два разных пользователя Linux не могут влиять на работу друг друга, а это и есть самое главное хорошее отличие систем Unix от большинства других систем.

Почти всем сложным программам для работы требуется входные параметры (опции, аргументы), от значения которых строится последовательность работы алгоритма заложенного в программе или используются различные источники данных.

Передача параметров в программу на C/C++ осуществляется через массив функции main(). Он называется argv (от arguments values - значения аргументов), но в принципе его можно назвать и по другому. Количество этих параметров передается через переменную argc (от arguments counter - счетчик аргументов).

Программа, для работы которой требуется набор входных параметров задается при помощи специального определения функции main():

int main(int argc, char *argv[]{

};


int main(int argc, char **argv){

};

В качестве напишем программку, которая выводит значения переданных параметров:

// программа test.c


#include <stdio.h>

int main(int argc, char *argv[]){

int i=0;

for (i=0;i<argc;i++){

printf("Argument %d: %s\n",i,argv[i]);

};

};

Сохраняем в файл test.c и компилируем:

dron:~# gcc test.c -o test

После этого попробуем запустить программу:

dron:~# ./test

Argument 0: ./test

Передадим несколько параметров:

dron:~# ./test qwe sdf fgh hjk kl 123 --help

Argument 0: ./test

Argument 1: qwe

Argument 2: sdf

Argument 3: fgh

Argument 4: hjk

Argument 5: kl

Argument 6: 123

Argument 7: --help

В качестве первого параметра программе всегда передается ее имя и таким образом программа может узнать свое название, т.е. имя файла, в котором она содержится.

Рассмотрим теперь, как работать с переданными параметрами. В системе Linux существует два вида параметров: короткие и длинные. Короткие параметры начинаются с одного дефиса и имеют длину в один символ, их просто и быстро набирать в командной строке. Длинные параметры начинаются с двух дефисов и могут иметь длинное имя, которое целесообразно использовать в скриптах (чтобы потом можно было вспомнить, что и как происходит). Кроме этого любой параметр может иметь значение, а может и не иметь. Ниже приведен пример несколько параметров:


-h - короткий параметр

--help - длинный параметр


-s 10 - параметры со значениями

--size 10

--size=10

Существует несколько специальных функций предназначенных для разбора списка переданных параметров:

  • int getopt(...) - Обрабатывает короткие параметры

  • int getopt_long(...) - Обрабатывает короткие и длинные параметры

  • int getopt_long_only(...) - Обрабатывает параметры только как длинные

Разберемся с работой первой функции - getopt(...). Ее определение выглядит следующим образом:

#include <unistd.h>


int getopt(int argc, char * const argv[],

const char *optstring);


extern char *optarg;

extern int optind, opterr, optopt;

Эта функция последовательно перебирает переданные параметры в программу. Для работы в функцию передается количество параметров argc, массив параметров argv[] и специальная строка optstring, в которой перечисляются названия коротких параметров и признаки того, что параметры должны иметь значение. Например, если программа должна воспринимать три параметра a, b, F , то такая строка бы выглядела как "abF". Если параметр должен иметь значение, то после буквы параметра ставится двоеточие, например параметр F и d имеют значения, а параметры e, a и b не имеют, тогда эта строка могла бы выглядеть как "eF:ad:b". Если параметр может иметь (т.е. может и не иметь) значение, то тогда ставится два знака двоеточия, например "a::" (это специальное расширение GNU). Если optstring содержит "W:", то тогда параметр -W opt переданный в программу, будет восприниматься как длинный параметр --opt. Это связано с тем, что параметр W зарезервирован в POSIX.2 для расширения возможностей.

Для перебора параметров функцию getopt() надо вызывать в цикле. В качестве результата возвращется буква названия параметра, если же параметры кончились, то функция возвращает -1. Индекс текущего параметра хранится в optind, а значение параметра помещается в optarg (указатель просто указывает на элемент массива argv[]). Если функция находит параметр не перечисленный в списке, то выводится сообщение об ошибке в stderr и код ошибки сохраняется в opterr, при этом в качестве значения возврящается "?". Вывод ошибки можно запретить, если установить opterr в 0.

#include <stdio.h>

#include <unistd.h>


int main(int argc, char *argv[]){

int rez=0;


// opterr=0;

while ( (rez = getopt(argc,argv,"ab:C::d")) != -1){

switch (rez){

case 'a': printf("found argument \"a\".\n"); break;

case 'b': printf("found argument \"b = %s\".\n",optarg); break;

case 'C': printf("found argument \"C = %s\".\n",optarg); break;

case 'd': printf("found argument \"d\"\n"); break;

case '?': printf("Error found !\n");break;

};

};


};

Попробуем скомпилировать данную программку и запустить:

dron:~# gcc test.c -o test


dron:~# ./test -a -b -d -C

found argument "a".

found argument "b = -d".

found argument "C = (null)".


dron:~# ./test -a -b -C -d

found argument "a".

found argument "b = -C".

found argument "d"


dron:~# ./test -a -b1 -C -d

found argument "a".

found argument "b = 1".

found argument "C = (null)".

found argument "d"


dron:~# ./test -b1 -b2 -b 15

found argument "b = 1".

found argument "b = 2".

found argument "b = 15".

Рассмотрим, как функция getopt вылавливает ошибки. Попробуем задать параметр, которого нет в списке:

dron:~# ./test -h -a

./test: invalid option -- h

Error found !

found argument "a".

Функция вывела сообщение об ошибке в stderr. Давайте выключим вывод сообщений, для этого надо где-то в программе перед вызовом функции вставить opterr=0;. Компилируем и запускаем:


dron:~# ./test -h -a

Error found !

found argument "a".

Теперь, как видите, сообщение больше не выдается, зато как и раньше можно обработать ошибку самому.






Приложение2.

Основы использования утилиты MAKE.

Утилита ‘make’ автоматически определяет, какие части составной программы (программы, состоящей из нескольких файлов) необходимо перекомпилировать, и выполняет их команды для перекомпиляции.

Далее использование ‘make’ будет показано на С программах, так как они наиболее часто используются в Linux, но вы можете испльзовать ‘make’ с любым другим языком программирования, чей компилятор может работать с командами интерпретатора. Конечно ‘make’ неограничена программированием. Вы можете ее использовать для описания любой задачи, в которой некоторые файлы должны обновляться автоматически , всякий раз когда изменяютя файлы, от которых они зависят.

Для подготовки использования ‘make’, необходимо создать файл, называемый “makefile” или “Makefile” , который описывает зависимости среди файлов, составляющих вашу программу и задает команды для обновления каждого файла. При создании программы на любом языке программирования, обычно, исполнимый файл обновляется ( или создается) из объектных файлов, которые в свою очередь обновляются (создаются) посредством компиляции файлов исходных текстов программы.

Если существует подходящий makefile , при каждом изменении некоторых исходных файлов, составляющих программу, команда оболочки

make

достаточна для выполнения всех необходимых перекомпиляций. Программа make использует базу данных ,описанную в makefile и дату последней модификации файлов для решения, какие файлы необходимо обновить. Для каждого такого файла, она выполняет команды записанные в базе данных makefile.


Далее будет рассмотрен простой makefile , который описывает как компилировать и линковать текстовый редактор, который состоит из восьми файлов исходных текстов на языке С и трех заголовочных файлов.

Когда ‘make’ перекомпилирует редактор, каждый измененный файл исходных текстов должен быть перекомпилирован. Если заголовочный файл был изменен, каждый файл исходных текстов, включающий этот зоголовочный файл должен быть перекомпилирован. Каждая процедура компиляции создает объектный файл соответствующий исходному. Наконец, если любой файл исходных текстов был перекомпилирован, все объектные файлы, и вновь созданные и осташиеся от предыдущей компиляции, должны быть собраны вместе линковщиком для создания нового исполнимого файла текстового редактора.

Простой makefile состоит из правил следующего вида:


ЦЕЛЬ ... : ЗАВИСИМОСТИ ...

КОМАНДЫ

...

...


ЦЕЛЬ – это обычно имя файла, который создается программой; примерами ЦЕЛИ являются исполнимые и объектные файлы. ЦЕЛЬ также может быть именем действия , которое необходимо выполнить, такое как ‘clean’ (очистка от ненужных файлов).

ЗАВИСИМОСТИ – это файлы, кторые используются как исходные для создания файла цели. ЦЕЛЬ обычно зависит от нескольких файлов.