Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SP_Intro0(1).pdf
Скачиваний:
18
Добавлен:
11.02.2016
Размер:
487.54 Кб
Скачать

Код главной программы готов. Требуется его откомпилировать с использованием библиотеки dl: dron:~# gcc main.c -o main -ldl

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

dron:~# ./main power2 power2(3.000000) = 9.000000 dron:~# ./main power3 power3(3.000000) = 27.000000 dron:~# ./main power4 power4(3.000000) = 81.000000 dron:~#

Ну, как ?! Круто !!! Мы используем какие-то функции, зная лишь их название. Представьте открывающиеся возможности для программ, на основе этого метода можно создавать плагины для программ, модернизировать какие-то части, добавлять новые возможности и многое другое.

Шаг 9 - Инициализация динамических библиотек

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

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

void _init() - инициализация

void _fini() - деинициализация

Чтобы понять, что к чему, введем в нашей библиотеке lib.c переменную test и возвращающую ее функцию: char *test;

char *ret_test(){ return test;

};

Пишем основную программу main.c. Она очень похожа на предыдущий наш проект, поэтому можете его модифицировать:

#include <stdio.h> #include <dlfcn.h>

int main(){

void *ext_library; double value=0; char * (*ret_test)();

ext_library = dlopen("libtest.so",RTLD_LAZY); if (!ext_library){

fprintf(stderr,"dlopen() error: %s\n", dlerror()); return 1;

};

ret_test = dlsym(ext_library,"ret_test");

printf("Return of ret_test: \"%s\" [%p]\n",(*ret_test)(),(*ret_test)());

dlclose(ext_library); };

После компиляции всего этого хозяйства мы получим результат: dron:~# gcc -c lib.c -fPIC

dron:~# gcc -shared lib.o -o libtest.so dron:~# gcc -o main main.c -ldl dron:~# ./main

Return of ret_test: "(null)" [(nil)] dron:~#

Как видите переменная test оказалась равной NULL, а нам бы хотелось нечто другое. Ну, так давайте посмотрим как работают функции _init() и _fini(). Создадим вторую библиотеку lib1.c:

#include <stdlib.h>

char *test;

char *ret_test(){ return test;

};

void _init(){ test=(char *)malloc(6); if (test!=NULL){

*(test+0)='d';

*(test+1)='r';

*(test+2)='o';

*(test+3)='n';

*(test+4)='!';

*(test+5)=0;

};

printf("_init() executed...\n"); };

void _fini(){

if (test!=NULL) free(test); printf("_fini() executed...\n"); };

Теперь пробуем компилировать: dron:~# gcc -c lib1.c -fPIC

dron:~# gcc -shared lib1.o -o libtest.so lib1.o: In function `_init':

lib1.o(.text+0x24): multiple definition of `_init' /usr/lib/crti.o(.init+0x0): first defined here lib1.o: In function `_fini':

lib1.o(.text+0xc0): multiple definition of `_fini' /usr/lib/crti.o(.fini+0x0): first defined here collect2: ld returned 1 exit status

dron:~#

Опаньки... Облом. Что же это такое ?! Оказывается кто-то уже использовал эти функции до нас и программа не может слинковаться. После долгого копания в нескольких чужих исходниках я получил ответ на этот вопрос. Оказывается, чтобы избавиться от мешающей библиотеки надо использовать ключ компилятора -nostdlib. Попробуем:

dron:~# gcc -shared -nostdlib lib1.o -o libtest.so dron:~#

Смотрите-ка, все прекрасно скомилировалось. Теперь попробуем запустить main: dron:~# ./main

_init() executed...

Return of ret_test: "dron!" [0x8049c20] _fini() executed...

dron:~#

Ну как ? Помоему классно. Теперь можно спокойно создавать для работы библиотеки правильно инициализированные переменные. Однако классные эти штуки - динамические библиотеки ! А Вы что хотели ? Тот же Windows только на своих DLL и живет. А Linux ничем не хуже...

Шаг 10 - Передача опций в программу - getopt

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

Вы наверняка знаете, что передача параметров в программу на 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;. Компилируем и запускаем:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]