Пользование динамическими библиотеками

Когда необходимо пользоваться динамической библиотекой в продукте, необходимо установить библиотеку в компьютере. Можно пользоваться динамическими библиотеками как зависимыми библиотеками (путем указания их в строке ссылки продукта) или поскольку время выполнения загрузило библиотеки (путем загрузки их, когда они необходимы, с помощью dlopen(3) OS X Developer Tools Manual Page).

Эта статья описывает процесс установки и пользования динамическими библиотеками. Это основывается на Оценках динамическая библиотека и StarMeals, StarMeals2, и Grades программы, включенные в пакет сопутствующего файла этого документа. Эта статья также показывает, как пользоваться динамическими библиотеками как зависимыми библиотеками или как загруженные временем выполнения библиотеки. Наконец, эта статья демонстрирует, как вмешаться функции, экспортируемые динамической библиотекой.

Установка зависимых библиотек

Прежде чем можно будет пользоваться динамической библиотекой как зависимой библиотекой, библиотека и ее заголовочные файлы должны быть установлены на компьютере. Стандартные расположения для заголовочных файлов ~/include, /usr/local/include и /usr/include. Стандартные расположения для динамических библиотек ~/lib, /usr/local/lib, и /usr/lib.

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

Для получения дополнительной информации о том, как добавить пути к этим переменным окружения, посмотрите Открытие Dynamic Libraries. Для приобретения знаний об установке зависимых библиотек в перемещаемом каталоге посмотрите Зависимые от предшествующего пути развития выполнением Библиотеки.

Если Вы не хотите изменять переменные окружения, и Вы хотите разместить динамическую библиотеку в нестандартное расположение, необходимо указать, куда в файловой системе Вы разместили библиотеку при соединении изображения. См. описание компилятора -dylib_file опция в http://gcc .gnu.org/onlinedocs/gcc/Darwin-Options.html#Darwin-Options для подробных данных.

Например, в OS X исполняемый код приложений может быть упакован вместе с платформами, содержащими библиотеки, создаваемые в частности для определенного приложения. Эти платформы известны как частные встроенные платформы. Приложения, использующие частные встроенные платформы, а также сами платформы, должны быть особенно созданы. См. “Создание Платформы” в Руководстве по программированию Платформы и “Загрузке Кода во Время выполнения” в Мужественных Темах Программирования для подробных данных.

Пользование Зависимыми библиотеками требует, чтобы Средние числа 1.1 и Оценки 1,1 динамических библиотеки были установлены на Вашем компьютере. Устанавливать эти библиотеки:

  1. Откройте пакет сопутствующего файла этого документа.

  2. В Терминале выполните эти команды:

    [Averages/1.1]% make install
    [Ratings/1.1]% make install

Пользование зависимыми библиотеками

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

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

Перечисление 1 показывает исходный код маленькой программы что Оценки использования 1.1, разработанный в Создании Динамических Библиотек.

Перечисление 1  Используя Оценки 1.1 как зависимая библиотека

/* File: StarMeals.c
 * Uses functions in libRatings.A.dylib.
 **************************************/
 
#include <stdio.h>
#include <string.h>
#include <Ratings.h>
 
#define MAX_NAMES 100
#define MAX_NAME_LENGTH 30
#define MAX_RATING_LENGTH 5
 
static char* name_list[MAX_NAMES];
static char* rating_list[MAX_NAMES];
static int names = 0;
 
void addNameAndRating(char* name, char* rating) {
    name_list[names] = strdup(name);
    rating_list[names] = (strlen(rating) > MAX_RATING_LENGTH)? "*****" : strdup(rating);
    names++;
}
 
void test_data(void) {
    addNameAndRating("Spinach", "*");
    addNameAndRating("Cake", "****");
    addNameAndRating("Steak", "****");
    addNameAndRating("Caviar", "*");
    addNameAndRating("Broccoli", "****");
    addNameAndRating("Gagh", "*****");
    addNameAndRating("Chicken", "*****");
}
 
int main(int argc, char* argv[]) {
    int test_mode = 0;
    if (argc == 2) {
        if (strcmp(argv[1], "test") == 0) {
            test_mode = 1;
            printf("[start_test]\n");
            test_data();
        }
    }
    else {
        printf("Enter meal names and ratings in the form <name> <rating>.\n");
        printf("No spaces are allowed in the name and the rating.\n");
        printf("The rating can be * through *****.\n");
        printf("To finish, enter \"end\" for a meal name.\n");
        while (names < MAX_NAMES) {
            char name[MAX_NAME_LENGTH];
            char rating[MAX_RATING_LENGTH + 1];
            printf("\nName and rating: ");
            scanf("%s", &name);
            if (strcmp(name, "end") == 0) {
                break;
            }
            scanf("%s", rating);
            addNameAndRating(name, rating);
        }
        printf("\n");
    }
 
    if (names) {
        // Print data entered and call libRatings.addRating().
        printf("This is the data you entered:\n");
        for (int i = 0; i < names; i++) {
            printf("%s (%s)\n", name_list[i], rating_list[i]);
            addRating(rating_list[i]);
        }
 
        // Print statistical information.
        printf("\nThe mean rating is %s\n", meanRating()); // 1
        if (medianRating) {                                // 2
            printf("The median rating is %s\n", medianRating());
        }
        if (frequentRating) {                              // 3
            printf("The most frequent rating is %s\n", frequentRating());
        }
 
        //printf("\n");
    }
 
    if (test_mode) {
        printf("[end_test]\n");
    }
    return 0;
}

Этот список описывает выделенные строки:

Скомпилировать StarMeals.c файл, используйте команду, показанную в Перечислении 2.

Перечисление 2  Компилирующий и соединяющийся StarMeals

[StarMeals]% make
clang -std=gnu99 StarMeals.c <user_home>/lib/libRatings.dylib <user_home>/lib/libAverages.dylib -o StarMeals

Заметьте что точное расположение библиотеки StarMeals непосредственно зависит от (libRatings.dylib) предоставлен в строке ссылки. Путь <user_home>/lib/libRatings.dylib фактически символьная ссылка на <user_home>/lib/libRatings.A.dylib. Во время ссылки статический компоновщик разрешает ссылку и хранит фактическое имя файла библиотеки в изображении, которое это генерирует. С этим подходом динамический компоновщик всегда использует полное имя библиотеки, когда это ищет зависимые библиотеки изображения.

Перечисление 3 показывает, что вывод StarMeals производит, когда выполнено в тестовом режиме:

  Вывод Listing 3 Test StarMeals программа

> ./StarMeals test
start_test
This is the data you entered:
Spinach (*)
Cake (****)
Steak (****)
Caviar (*)
Broccoli (****)
Gagh (*****)
Chicken (*****)
 
The mean rating is ***
The medianRating is ****
The most frequent rating is ****
[end_test]

Пользование загруженными временем выполнения библиотеками

Изображение, пользующееся динамическими библиотеками как загруженными временем выполнения библиотеками, меньше и загружается быстрее, чем изображение, пользующееся теми же библиотеками как зависимые библиотеки. Статический компоновщик не добавляет информацию о загруженных временем выполнения библиотеках к изображению. Когда изображение загружается, и динамический загрузчик не должен загружать зависимые библиотеки библиотеки. Однако эта гибкость прибывает в цену. Прежде чем изображение может пользоваться динамической библиотекой, которая не является одной из ее зависимых библиотек, это должно загрузить библиотеку dlopen(3) OS X Developer Tools Manual Page и получите адрес каждого символа, с которым он нуждается dlsym(3) OS X Developer Tools Manual Page. Изображение должно также вызвать dlclose(3) OS X Developer Tools Manual Page когда это сделало пользование библиотекой.

Программа StarMeals2 обеспечивает ту же функциональность, которую обеспечивает StarMeals. Но StarMeals2 использует Оценки 1,1 динамических библиотеки, поскольку время выполнения загрузило библиотеку. Перечисление 4 показывает исходный код программы.

Перечисление 4  Используя Оценки 1.1 как загруженная временем выполнения библиотека

/* File: StarMeals2.c
 * Uses functions in libRatings.A.dylib.
 **************************************/
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <Ratings.h>
 
#define MAX_NAMES 100
#define MAX_NAME_LENGTH 30
#define MAX_RATING_LENGTH 5
 
static char *name_list[MAX_NAMES];
static char *rating_list[MAX_NAMES];
static int names = 0;
 
void addNameAndRating(char *name, char *rating) {
    name_list[names] = strdup(name);
    rating_list[names] =
        (strlen(rating) > MAX_RATING_LENGTH)?
        "*****" : strdup(rating);
    names++;
}
 
void test_data(void) {
    addNameAndRating("Spinach", "*");
    addNameAndRating("Cake", "****");
    addNameAndRating("Steak", "****");
    addNameAndRating("Caviar", "*");
    addNameAndRating("Broccoli", "****");
    addNameAndRating("Gagh", "*****");
    addNameAndRating("Chicken", "*****");
}
 
int main(int argc, char *argv[]) {
    int test_mode = 0;
    if (argc == 2) {
        if (strcmp(argv[1], "test") == 0) {
            test_mode = 1;
            printf("[start_test]\n");
            test_data();
        }
    }
    else {
        printf("Enter restaurant names and ratings in the form <name> <rating>.\n");
        printf("No spaces are allowed in the name and the rating.\n");
        printf("The rating can be * through *****.\n");
        printf("To finish, enter \"end\" for a restaurant name.\n");
        while (names < MAX_NAMES) {
            char name[MAX_NAME_LENGTH];
            char rating[MAX_RATING_LENGTH + 1];
            printf("\nName and rating: ");
            scanf("%s", &name);
            if (strcmp(name, "end") == 0) {
                break;
            }
            scanf("%s", rating);
            addNameAndRating(name, rating);
        }
        printf("\n");
    }
 
    if (names) {
        // Open Ratings library.
        void* lib_handle = dlopen("libRatings.A.dylib", RTLD_LOCAL|RTLD_LAZY);
        if (!lib_handle) {
            printf("[%s] Unable to load library: %s\n", __FILE__, dlerror());
            exit(EXIT_FAILURE);
        }
 
        // Print data entered and call libRatings.A:addRating().
        void (*addRating)(char*) = dlsym(lib_handle, "addRating");
        if (!addRating) {       // addRating is guaranteed to exist in libRatings.A.dylib
            printf("[%s] Unable to get symbol: %s\n", __FILE__, dlerror());
            exit(EXIT_FAILURE);
        }
        printf("This is the data you entered:\n");
        for (int i = 0; i < names; i++) {
            printf("%s (%s)\n", name_list[i], rating_list[i]);
            addRating(rating_list[i]);
        }
 
        // Print statistical information.
        char *(*meanRating)(void) = dlsym(lib_handle, "meanRating");
        if (!meanRating) {      // meanRating is guaranteed to exist in libRatings.A.dylib
            printf("[%s] Unable to get symbol: %s\n", __FILE__, dlerror());
            exit(EXIT_FAILURE);
        }
        printf("\nThe mean rating is %s\n", meanRating());
 
        char *(*medianRating)(void) = dlsym(lib_handle, "medianRating");
        if (medianRating) {     // Backwards compatibility with Ratings 1.0
            printf("The median rating is %s\n", medianRating());
        }
        char *(*frequentRating)(void) = dlsym(lib_handle, "frequentRating");
        if (frequentRating) {   // Backwards compatibility with Ratings 1.0
            printf("The most frequent rating is %s\n", frequentRating());
        }
 
        // Close Ratings library
        if (dlclose(lib_handle) != 0) {
            printf("[%s] Problem closing library: %s", __FILE__, dlerror());
        }
    }
 
    if (test_mode) {
        printf("[end_test]\n");
    }
    return 0;
}

Перечисление 5 показывает компиляции StarMeals2 программа.

  Компиляция перечисления 5 и соединение StarMeals2

[StarMeals2]% make
clang -std=gnu99 StarMeals2.c -I<user_home>/include -o StarMeals2

Статический компоновщик не жалуется на неразрешенные внешние ссылки в libRatings.A.dylib потому что это не включено в строку ссылки. Когда StarMeals2 использует, динамический компоновщик разрешает эти ссылки dlopen(3) OS X Developer Tools Manual Page загружаться libRatings.A.dylib.

Вмешивающиеся функции в зависимых библиотеках

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

Для вызывания вмешавшейся функции из пользовательского определения Вы используете dlsym(RTLD_NEXT, "<function_name>") вызовите для получения адреса «реальной» функции. Например, Перечисление 6 показывает, как можно записать пользовательскую версию функции, определяемой в динамической библиотеке.

Перечисление 6  , Вмешивающееся функция

char *name(void) {
    static int name_calls = 0;
    printf("[STATS] name() has been called %i times\n", name_calls);
    char *(*next_name)(void) = dlsym(RTLD_NEXT, "name");
    return next_name();
}

Можно использовать вмешательство для адаптации существующей динамической библиотеки к определенным потребностям, не изменяя ее API. Например, сопутствующий пакет этого документа включает реализации двух динамических библиотек под названием Оценки и RatingsAsGrades. Библиотека Ratings реализует основанную на звезде систему оценки (она может использоваться для соответствия оценкам ресторана и отеля; например, *** и *****). Библиотека RatingsAsGrades реализует основанную на букве систему классификации, которая может использоваться для соответствия студенческим классам; например, A и C. Вместо того, чтобы писать новый алгоритм для управления буквенными оценками библиотека RatingsAsGrades эффективно использует функциональность библиотеки Ratings. Перечисление 7 показывает интерфейс и реализацию библиотеки RatingsAsGrades.

Перечисление 7  RatingsAsGrades, вмешивающийся оценки

/* File: RatingsInterposed.h
 * Interface to the RatingsAsGrages dynamic library.
 **************************************************/
 
/* Adds 'grade' to the set.
 *      grade: Non-NULL string. Contains zero or
 *             one of these characters:
 *             A, B, C, D, F.
 *             Examples: "", "A", "B".
 */
void addRating(char* grade);
 
/* Returns the median grade.
 */
char *medianRating(void);
 
/* Returns the most frequent grade.
 */
char *frequentRating(void);
 
/* File: RatingsAsGrades.c
 * Compile with -fvisibility=hidden.
 ***********************************/
 
#include "RatingsAsGrades.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
 
#define EXPORT __attribute__((visibility("default")))
#define MAX_STAR_RATING_LEN 6
 
static char *_ratingAsGrade(char *rating) {
    int rating_length = strlen(rating);
    if (rating_length > MAX_STAR_RATING_LEN - 1) {
        rating_length = MAX_STAR_RATING_LEN - 1;
    }
    char grade;
    switch (rating_length) {
        case 5:
            grade = 'A';
            break;
        case 4:
            grade = 'B';
            break;
        case 3:
            grade = 'C';
            break;
        case 2:
            grade = 'D';
            break;
        case 1:
            grade = 'F';
            break;
        default:
            grade = '\0';
    }
    char char_grade[2] = { grade, '\0' };
    return strdup(char_grade);
}
 
// Interpose libRatings.B.dylib:addRating.
EXPORT
void addRating(char* grade) {
    char rating[MAX_STAR_RATING_LEN] = { '\0' };
    switch (*grade) {
        case 'A':
            strcat(rating, "*");
        case 'B':
            strcat(rating, "*");
        case 'C':
            strcat(rating, "*");
        case 'D':
            strcat(rating, "*");
        case 'F':
            strcat(rating, "*");
        default:
            ;
    }
    void (*next_addRating)(char *) =
dlsym(RTLD_NEXT, "addRating");
    if (next_addRating) {
        next_addRating(rating);
    }
    else {
        printf("[%s] Fatal problem: %s", __FILE__, dlerror());
    }
}
 
// Interpose libRatings.B.dylib:medianRating.
EXPORT
char *medianRating(void) {
    char medianGrade[2] = { '\0' };
    char *(*next_medianRating)(void) =
dlsym(RTLD_NEXT, "medianRating");
    if (next_medianRating) {
        strcpy(medianGrade,
_ratingAsGrade(next_medianRating()));
    }
    else {
        printf("[%s] Fatal problem: %s", __FILE__, dlerror());
        exit(EXIT_FAILURE);
    }
    return strdup(medianGrade);
}
 
// Interpose libRatings.B.dylib:frequentRating.
EXPORT
char *frequentRating(void) {
    char frequentGrade[2] = { '\0' };
    char *(*next_frequentRating)(void) =
        dlsym(RTLD_NEXT, "frequentRating");
    if (next_frequentRating) {
        strcpy(frequentGrade,
            _ratingAsGrade(next_frequentRating()));
    }
    else {
        printf("[%s] Fatal problem: %s", __FILE__, dlerror());
        exit(EXIT_FAILURE);
    }
    return strdup(frequentGrade);
 
}

Заметьте как addRating, medianRating, и frequentRating функции, измените ввод и вывод определений они тень.

Пакет сопутствующих файлов включает исходный код Grades программа. Эта программа пользуется библиотекой RatingsAsGrades для соответствия классам студентов.

Следуйте этим инструкциям, чтобы создать и работать Grades программа:

  1. Откройте пакет сопутствующих файлов этого документа.

  2. В Терминале выполните эти команды:

    [Ratings/1.1]% make install
    [Grades]% make

Перечисление 8 показывает вывод Grades программа, когда работал в тестовом режиме.

  Вывод Listing 8 Test Grades программа

[Grades]% ./Grades test
[start_test]
This is the data you entered:
Eloise  (F)
Karla   (B)
Iva     (B)
Hilaire (F)
Jewel   (B)
Simone  (A)
Yvette  (A)
Renee   (A)
Mimi    (A)
 
The median grade is B
The most frequent grade is A
[end_test]