Пользовательские сборки SQLite
или
Портирование SQLite на новые операционные системы
1.0 Введение
Для большинства приложений рекомендуемый способ построения SQLite — использование файла кода объединения объединения, sqlite3.c, и соответствующего файла заголовков sqlite3.h. Файл кода sqlite3.c должен компилироваться и работать на любой системе Unix, Windows без каких-либо изменений или специальных параметров компилятора. Большинство приложений могут просто включить файл sqlite3.c вместе с другими файлами C-кода, составляющими приложение, скомпилировать их все вместе и получить работающую и правильно настроенную версию SQLite.
Большинство приложений прекрасно работают с SQLite в его стандартной конфигурации и без специальной конфигурации во время компиляции. Большинство разработчиков должны уметь полностью игнорировать этот документ и просто скомпилировать SQLite из объединения без каких-либо специальных знаний и без каких-либо специальных действий.
Однако, высоконастроенные и специализированные приложения могут захотеть или потребовать заменить некоторые встроенные системные интерфейсы SQLite альтернативными реализациями, более подходящими для нужд приложения. SQLite разработан для легкой переконфигурации во время компиляции, чтобы удовлетворить специфические потребности отдельных проектов. Среди параметров конфигурации SQLite во время компиляции есть следующие:
Замена встроенной подсистемы мьютексов альтернативной реализацией.
Полное отключение всех мьютексов для использования в однопоточных приложениях.
Переконфигурация подсистемы выделения памяти для использования другого выделения памяти вместо реализации malloc() из стандартной библиотеки.
Переопределение подсистемы выделения памяти так, чтобы она никогда не вызывала malloc(), а вместо этого удовлетворяла все запросы памяти с помощью буфера памяти фиксированного размера, назначенного SQLite при запуске.
Замена интерфейса файловой системы альтернативной конструкцией. Другими словами, переопределение всех системных вызовов, которые делает SQLite для общения с диском, совершенно другой набором системных вызовов.
Переопределение других интерфейсов операционной системы, таких как вызовы для получения времени Zulu или локального времени.
В целом, в SQLite есть три отдельные подсистемы, которые можно изменять или переопределять во время компиляции. Подсистема мьютексов используется для сериализации доступа к ресурсам SQLite, общим для нескольких потоков. Подсистема выделения памяти используется для выделения памяти, необходимой для объектов SQLite и кэша базы данных. Наконец, подсистема Виртуальной файловой системы используется для предоставления портативного интерфейса между SQLite и базовой операционной системой, а особенно файловой системой. Мы называем эти три подсистемы "интерфейсными" подсистемами SQLite.
Мы подчеркиваем, что для большинства приложений подходят встроенные реализации подсистем интерфейса SQLite по умолчанию. Разработчикам рекомендуется использовать встроенные реализации по умолчанию, когда это возможно, и компилировать SQLite без каких-либо специальных параметров или опций во время компиляции. Однако, некоторые высокоспециализированные приложения могут извлечь выгоду из замены или изменения одной или нескольких из этих встроенных подсистем интерфейса SQLite. Либо, если SQLite используется в операционной системе, отличной от Unix (Linux или Mac OS X), Windows (Win32 или WinCE), или OS/2, то ни одна из встроенных подсистем интерфейса SQLite не будет работать, и приложение должно предоставить альтернативные реализации, подходящие для целевой платформы.
2.0 Настройка или замена подсистемы мьютексов
В многопоточной среде SQLite использует мьютексы для сериализации доступа к общим ресурсам. Подсистема мьютексов требуется только для приложений, которые обращаются к SQLite из нескольких потоков. Для однопоточных приложений или приложений, которые обращаются к SQLite только из одного потока, подсистема мьютексов может быть полностью отключена путем повторной компиляции со следующим параметром:
-DSQLITE_THREADSAFE=0
Мьютексы недороги, но они не бесплатны, поэтому производительность будет лучше, когда мьютексы полностью отключены. Результирующий размер библиотеки также будет немного меньше. Отключение мьютексов во время компиляции — рекомендуемая оптимизация для приложений, где это имеет смысл.
При использовании SQLite как динамической библиотеки приложение может проверить, отключены ли мьютексы, используя API sqlite3_threadsafe(). Приложения, которые связываются с SQLite во время выполнения и используют SQLite из нескольких потоков, должны, вероятно, проверить этот API, чтобы убедиться, что они случайно не связаны с версией библиотеки SQLite, у которой отключены мьютексы. Однопоточные приложения, конечно, будут работать правильно независимо от того, настроено ли SQLite быть потокобезопасным, хотя они будут немного быстрее при использовании версий SQLite с отключенными мьютексами.
Мьютексы SQLite также можно отключить во время выполнения, используя интерфейс sqlite3_config(). Чтобы полностью отключить все мьютексы, приложение может вызвать:
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
Отключение мьютексов во время выполнения не так эффективно, как отключение их во время компиляции, поскольку SQLite все равно должен выполнить проверку булевой переменной, чтобы увидеть, включены или отключены мьютексы в каждой точке, где может потребоваться мьютекс. Но все же есть преимущество производительности при отключении мьютексов во время выполнения.
Для многопоточных приложений, которые тщательно следят за тем, как они управляют потоками, SQLite поддерживает альтернативную конфигурацию во время выполнения, которая находится на полпути между отсутствием мьютексов и стандартной ситуацией мьютексов. Этот промежуточный режим мьютексов может быть установлен следующим образом:
sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_config(SQLITE_CONFIG_MEMSTATUS, 0);
Здесь есть два отдельных изменения конфигурации, которые могут быть использованы вместе или по отдельности. Настройка SQLITE_CONFIG_MULTITHREAD отключает мьютексы, которые сериализуют доступ к объектам соединения с базой данных и объектам подготовленных запросов. С этим параметром приложение свободно может использовать SQLite из нескольких потоков, но оно должно убедиться, что два потока не пытаются получить доступ к одному соединению с базой данных или каким-либо подготовленным запросам, связанным с тем же соединением с базой данных, одновременно. Два потока могут использовать SQLite одновременно, но они должны использовать отдельные соединения с базой данных. Вторая настройка SQLITE_CONFIG_MEMSTATUS отключает механизм в SQLite, который отслеживает общий размер всех ожидающих запросов выделения памяти. Это исключает необходимость мьютексирования каждого вызова sqlite3_malloc() и sqlite3_free(), что экономит огромное количество операций мьютексирования. Но следствием отключения механизма статистики памяти является то, что интерфейсы sqlite3_memory_used(), sqlite3_memory_highwater() и sqlite3_soft_heap_limit64() перестают работать.
SQLite использует pthreads для реализации мьютексов в Unix, и SQLite требует рекурсивного мьютекса. Большинство современных реализаций pthreads поддерживают рекурсивные мьютексы, но не все. Для систем, которые не поддерживают рекурсивные мьютексы, рекомендуется, чтобы приложения работали только в однопоточном режиме. Если это невозможно, SQLite предоставляет альтернативную реализацию рекурсивного мьютекса, построенную на основе стандартных "быстрых" мьютексов pthreads. Эта альтернативная реализация должна работать правильно, если pthread_equal() является атомной, и процессор имеет согласованный кэш данных. Альтернативная реализация рекурсивного мьютекса включена следующим флагом компилятора:
-DSQLITE_HOMEGROWN_RECURSIVE_MUTEX=1
При портировании SQLite на новую операционную систему обычно необходимо полностью заменить встроенную подсистему мьютексов альтернативой, основанной на примитивах мьютексов новой операционной системы. Это достигается путем компиляции SQLite с указанным параметром:
-DSQLITE_MUTEX_APPDEF=1
Когда SQLite скомпилирован с параметром SQLITE_MUTEX_APPDEF=1, он полностью опускает реализацию своих функций примитива мьютекса. Но библиотека SQLite по-прежнему пытается вызвать эти функции при необходимости, поэтому приложение должно само реализовать функции примитива мьютекса и связать их вместе с SQLite.
3.0 Настройка или замена подсистемы выделения памяти
По умолчанию SQLite получает необходимую память для объектов и кэша из реализации malloc()/free() стандартной библиотеки. Также ведутся работы с экспериментальными выделениями памяти, которые удовлетворяют всем запросам памяти из одного фиксированного буфера памяти, переданного SQLite при запуске приложения. Дополнительная информация об этих экспериментальных выделениях памяти будет предоставлена в будущей редакции этого документа.
SQLite поддерживает возможность приложения указывать альтернативное выделение памяти во время выполнения, заполняя экземпляр объекта sqlite3_mem_methods указателями на функции альтернативной реализации, а затем регистрируя новую альтернативную реализацию с помощью интерфейса sqlite3_config(). Например:
sqlite3_config(SQLITE_CONFIG_MALLOC, &my_malloc_implementation);
SQLite создает копию содержимого объекта sqlite3_mem_methods, поэтому объект может быть изменен после возвращения вызова sqlite3_config().
4.0 Добавление новых виртуальных файловых систем
С версии 3.5.0 (2007-09-04) SQLite поддерживает интерфейс, называемый виртуальной файловой системой или "VFS". Этот объект несколько неправильно назван, поскольку это на самом деле интерфейс ко всей базовой операционной системе, а не только к файловой системе.
Одна из интересных особенностей интерфейса VFS заключается в том, что SQLite может поддерживать несколько VFS одновременно. Каждое соединение с базой данных должно выбрать одну VFS для своего использования при первом открытии соединения с помощью sqlite3_open_v2(). Но если процесс содержит несколько соединений с базой данных, каждое может выбрать разные VFS. VFS могут быть добавлены во время выполнения с помощью интерфейса sqlite3_vfs_register().
Стандартные сборки SQLite под Unix, Windows и OS/2 включают VFS, подходящую для целевой платформы. Сборки SQLite для других операционных систем по умолчанию не содержат VFS, но приложение может зарегистрировать одну или несколько во время выполнения.
5.0 Портирование SQLite на новую операционную систему
Для портирования SQLite на новую операционную систему — операционную систему, не поддерживаемую по умолчанию — приложение должно предоставить…
- работающую подсистему мьютексов (только если это многопоточная система),
- работающую подсистему выделения памяти (при условии, что в стандартной библиотеке отсутствует malloc()), и
- работающую реализацию VFS.
Все это можно предоставить в одном дополнительном файле C-кода, а затем связать его с файлом кода "sqlite3.c" для создания рабочей сборки SQLite для целевой операционной системы. В дополнение к альтернативным подсистемам мьютексов и выделения памяти, и новой VFS, дополнительный файл C-кода должен содержать реализации следующих двух функций:
Файл кода "sqlite3.c" содержит стандартные реализации VFS и функций sqlite3_initialize() и sqlite3_shutdown(), подходящих для Unix, Windows и OS/2. Чтобы предотвратить загрузку одного из этих компонентов по умолчанию при компиляции sqlite3.c, необходимо добавить следующий параметр компиляции:
-DSQLITE_OS_OTHER=1
Ядро SQLite вызовет функцию sqlite3_initialize() на ранней стадии. Вспомогательный файл C-кода может содержать реализацию функции sqlite3_initialize(), которая регистрирует соответствующий VFS и, возможно, инициализирует альтернативную систему мьютексов (если мьютексы необходимы) или выполняет инициализацию подсистемы выделения памяти, если это требуется. Ядро SQLite никогда не вызывает функцию sqlite3_shutdown(), но она является частью официального API SQLite и не предоставляется в других случаях, когда компилируется с -DSQLITE_OS_OTHER=1, поэтому вспомогательный файл C-кода должен, вероятно, предоставить ее для полноты.
SQLite is in the Public Domain.
https://sqlite.org/custombuild.html