Small. Fast. Reliable.
Choose any three.

SQLite的定制与移植
Custom Builds Of SQLite
or
Porting SQLite To New Operating Systems

1.0 介绍
1.0 Introduction

对于大多数应用程序来说,构建SQLite的推荐方法是使用混合代码文件——sqlite3.c,以及相对应的头文件sqlite3.h。sqlite3.c源码文件可以无需修改任何编译选项在Unix、Windows、OS/2、Mac OS X等系统上编译。多数应用程序只需要简单的将sqlite3.c文件包含到应用程序的其它C代码文件中,然后一起编译,然后就可以得到一个配置良好、可以使用的SQLite了。
For most applications, the recommended method for building SQLite is to use the amalgamation code file, sqlite3.c, and its corresponding header file sqlite3.h. The sqlite3.c code file should compile and run on any Unix, Windows, OS/2, or Mac OS X system without any changes or special compiler options. Most applications can simply include the sqlite3.c file together with the other C code files that make up the application, compile them all together, and have working and well configured version of SQLite.

大多数的应用程序无需使用任何特别的编译期配置,SQLite默认的配置就可以使其良好的工作了。大多数开发者完全可以彻底抛弃这篇文档,只要简单的使用混合代码构建SQLite就可以了,这不需要任何专业的知识或者其它任何专门的操作。
Most applications work great with SQLite in its default configuration and with no special compile-time configuration. Most developers should be able to completely ignore this document and simply build SQLite from the amalgamation without any special knowledge and without taking any special actions.

但是,高度特化和校调的应用程序可能会需要将SQLite一些内置的系统接口实现进行替换,以便更加符合应用的需求。在SQLite的设计中,在面对各种特定的项目的时候非常容易在编译期从新配置。SQLite的编译期配置包含下列这些内容:
However, highly tuned and specialized applications may want or need to replace some of SQLite's built-in system interfaces with alternative implementations more suitable for the needs of the application. SQLite is designed to be easily reconfigured at compile-time to meet the specific needs of individual projects. Among the compile-time configuration options for SQLite are these:

通俗来讲,在SQLite中包含三个独立的子系统,它们都可以在编译期进行修改或重载。互斥锁子系统是用来在多线程访问共享资源时进行访问串行化的。内存分配子系统是用来为数据库缓存和SQLite对象分配所需的内存的。最后,虚拟文件系统是用来在SQLite和底层的操作系统的特定文件系统之间提供一套可移植的接口。我们将这三个子系统称作SQLite的“接口”子系统。
Generally speaking, there are three separate subsystems within SQLite that can be modified or overridden at compile-time. The mutex subsystem is used to serialize access to SQLite resources that are shared among threads. The memory allocation subsystem is used to allocate memory required by SQLite objects and for the database cache. Finally, the Virtual File System subsystem is used to provide a portable interface between SQLite and the underlying operating system and especially the file system. We call these three subsystems the "interface" subsystems of SQLite.

我们再次强调,多数系统使用SQLite接口子系统的内置默认实现就可以很好的工作了。我们鼓励开发者使用内置的默认实现,而无需在构建SQLite时使用任何编译期的选项或者参数。 但是,一些高度特化的应用程序可能可以从替换或者修改多个SQLite内置的接口子系统中获益。或者,如果使用SQLite的操作系统不是Unix(Linux、Mac OS X)、Windows(Win32、WinCE)或OS/2,那么内置的接口子系统都将无法工作,这时就需要应用程序来提供适用于目标平台的子系统实现了。
We emphasis that most applications are well-served by the built-in default implementations of the SQLite interface subsystems. Developers are encouraged to use the default built-in implementations whenever possible and to build SQLite without any special compile-time options or parameters. However, some highly specialized applications may benefit from substituting or modifying one or more of these built-in SQLite interface subsystems. Or, if SQLite is used on an operating system other than Unix (Linux or Mac OS X), Windows (Win32 or WinCE), or OS/2 then none of the interface subsystems that come built into SQLite will work and the application will need to provide alternative implementations suitable for the target platform.

2.0 配置或替换互斥锁子系统
2.0 Configuring Or Replacing The Mutex Subsystem

在多线程环境中,SQLite使用互斥锁来对共享资源的访问进行串行化。只有需要在多线程访问SQLite的应用程序才需要互斥锁子系统。对于单线程的应用或只会在单线程中调用SQLite的应用来说,完全可以使用下面选项重新编译SQLite来彻底禁用互斥体子系统:
In a multithreaded environment, SQLite uses mutexes to serialize access to shared resources. The mutex subsystem is only required for applications that access SQLite from multiple threads. For single-threaded applications, or applications which only call SQLite from a single thread, the mutex subsystem can be completely disabled by recompiling with the following option:

-DSQLITE_THREADSAFE=0

互斥锁的开销很小,但并不是没有,所以当完全禁用了互斥锁后性能会得到提升。最终的封装库大小也会变的小一些。当这些是有意义的情况下,推荐在编译期禁用互斥锁。
Mutexes are cheap but they are not free, so performance will be better when mutexes are completely disabled. The resulting library footprint will also be a little smaller. Disabling the mutexes at compile-time is a recommended optimization for applications where it makes sense.

当把SQLite作为共享库使用时,应用程序可以使用sqlite3_threadsafe() API来测试看互斥锁是否禁用了。所有在运行时连接SQLite并且会在多线程环境下使用SQLite的应用程序都应该检查一下这个API,以确保不会意外连入一个禁用了互斥锁的SQLite库版本。当然,单线程应用不管SQLite是否配置为线程安全都可以正确的运行,不过如果使用禁用互斥锁的SQLite版本能够运行的稍微快一些。
When using SQLite as a shared library, an application can test to see whether or not mutexes have been disabled using the sqlite3_threadsafe() API. Applications that link against SQLite at run-time and use SQLite from multiple threads should probably check this API to make sure they did not accidentally get linked against a version of the SQLite library that has its mutexes disabled. Single-threaded applications will, of course, work correctly regardless of whether or not SQLite is configured to be threadsafe, though they will be a little bit faster when using versions of SQLite with mutexes disabled.

SQLite互斥锁也可以在运行时使用sqlite3_config()接口来禁用。应用程序可以执行下面接口来完全禁用所有互斥锁:
SQLite mutexes can also be disabled at run-time using the sqlite3_config() interface. To completely disable all mutexing, the application can invoke:

sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);

在运行时禁用互斥锁并没有编译期禁用那么高效,这是因为SQLite在每个需要互斥锁的地方还必须做一个布尔值的判断来确认互斥锁是否禁用了。不过运行时禁用互斥锁依然可以带来性能提升。
Disabling mutexes at run-time is not as effective as disabling them at compile-time since SQLite still must do a test of a boolean variable to see if mutexes are enabled or disabled at each point where a mutex might be required. But there is still a performance advantage for disabling mutexes at run-time.

对于多线程应用程序,需要关心它们是如何管理线程的。SQLite支持一个可选的运行时配置,是位于不使用任何互斥锁和默认的对所有内容使用互斥锁的中间状态。这个“中间态”互斥锁可以使用下面的方式来建立:
For multi-threaded applications that are careful about how they manage threads, SQLite supports an alternative run-time configuration that is half way between not using any mutexes and the default situation of mutexing everything in sight. This in-the-middle mutex alignment can be established as follows:

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()接口都无法使用了。
There are two separate configuration changes here which can be used either togethr or separately. The SQLITE_CONFIG_MULTITHREAD setting disables the mutexes that serialize access to database connection objects and prepared statement objects. With this setting, the application is free to use SQLite from multiple threads, but it must make sure than no two threads try to access the same database connection or any prepared statements associated with the same database connection at the same time. Two threads can use SQLite at the same time, but they must use separate database connections. The second SQLITE_CONFIG_MEMSTATUS setting disables the mechanism in SQLite that tracks the total size of all outstanding memory allocation requests. This omits the need to mutex each call to sqlite3_malloc() and sqlite3_free(), which saves a huge number of mutex operations. But a consequence of disabling the memory statistics mechanism is that the sqlite3_memory_used(), sqlite3_memory_highwater(), and sqlite3_soft_heap_limit64() interfaces cease to work.

SQLite在Unix上使用pthread实现的互斥锁,并且SQLite中需要递归锁。大多数现代的pthread实现都支持递归锁,但并不是全部。对于不支持递归锁的系统来说,推荐应用程序只在单线程模式下运行。如果这不可能,SQLite提供了一个以pthread中的标准“快速”互斥锁为基础实现的递归锁。只要pthread_equal()是原子的,并且处理器有相干数据缓存,那么这个实现就可以正确的工作。这个可选的递归锁实现可以使用下面的编译器命令行开关来启用:
SQLite uses pthreads for its mutex implementation on Unix and SQLite requires a recursive mutex. Most modern pthread implementations support recursive mutexes, but not all do. For systems that do not support recursive mutexes, it is recommended that applications operate in single-threaded mode only. If this is not possible, SQLite provides an alternative recursive mutex implementation built on top of the standard "fast" mutexes of pthreads. This alternative implementation should work correctly as long as pthread_equal() is atomic and the processor has a coherent data cache. The alternative recursive mutex implementation is enabled by the following compiler command-line switch:

-DSQLITE_HOMEGROWN_RECURSIVE_MUTEX=1

当把SQLite移植到新的操作系统上时,通常需要使用一个新操作系统互斥锁原语构建的互斥锁子系统来完全替换内置的互斥锁子系统。这可以通过使用下面的选项编译SQLite来实现:
When porting SQLite to a new operating system, it is usually necessary to completely replace the built-in mutex subsystem with an alternative built around the mutex primitives of the new operating system. This is accomplished by compiling SQLite with the following option:

-DSQLITE_MUTEX_APPDEF=1

当使用 SQLITE_MUTEX_APPDEF=1选项编译SQLite时,会完全忽略内置的互斥锁原始函数实现。但是SQLite库在需要的时候依然会调用这些函数,所以应用程序必须自己实现这些互斥锁原始函数,并将其与SQLite链接到一起。
When SQLite is compiled with the SQLITE_MUTEX_APPDEF=1 option, it completely omits the implementation of its mutex primitive functions. But the SQLite library still attempts to call these functions where necessary, so the application must itself implement the mutex primitive functions and link them together with SQLite.

3.0 配置或替换内存分配子模块
3.0 Configuring Or Replacing The Memory Allocation Subsystem

默认情况,SQLite从标准库中的malloc()/free()实现来获取对象和缓存所需的内存。还有一个还处于实验状态的内存分配器,这个分配器可以使用在应用启动时分配给SQLite的一块儿固定大小的内存缓冲区来满足所有的内存分配请求。更多关于实验性内存分配器的内容将会在本文未来的版本中提供。
By default, SQLite obtains the memory it needs for objects and cache from the malloc()/free() implementation of the standard library. There is also on-going work with experimental memory allocators that satisfy all memory requests from a single fixed memory buffer handed to SQLite at application start. Additional information on these experimental memory allocators will be provided in a future revision of this document.

SQLite能够支持应用程序在运行时指定一个可选的内存分配器。首先将实现内存分配的函数的指针填入到一个sqlite3_mem_methods对象的实例中,然后将这个对象使用sqlite3_config()接口注册到系统中。例如:
SQLite supports the ability of an application to specify an alternative memory allocator at run-time by filling in an instance of the sqlite3_mem_methods object with pointers to the routines of the alternative implementation then registering the new alternative implementation using the sqlite3_config() interface. For example:

sqlite3_config(SQLITE_CONFIG_MALLOC, &my_malloc_implementation);

SQLite会将sqlite3_mem_methods的内容复制一份,所以在sqlite3_config()调用返回后就可以修改这个对象的内容了。
SQLite makes a copy of the content of the sqlite3_mem_methods object so the object can be modified after the sqlite3_config() call returns.

4.0 添加新的虚拟文件系统
4.0 Adding New Virtual File Systems

3.5.0 版开始,SQLite支持了一个叫做虚拟文件系统 或 "VFS"的接口。这个对象的名字取的有些不太恰当,因为这实际上是整个底层操作系统的接口,而不仅仅是文件系统。
Since version 3.5.0, SQLite has supported an interface called the virtual file system or "VFS". This object is somewhat misnamed since it is really an interface to the whole underlying operating system, not just the filesystem.

VFS接口的一个有趣的特征是,SQLite可以同时支持多个VFS。当每个数据库连接使用sqlite3_open_v2()接口第一次打开时,需要选择一个VFS供其使用。但是如果一个进程包含多个数据库连接,那么每个连接可以选择不同的VFS。可以再运行时通过sqlite3_vfs_register()接口来添加VFS。
One of the interesting features of the VFS interface is that SQLite can support multiple VFSes at the same time. Each database connection has to choose a single VFS for its use when the connection is first opened using sqlite3_open_v2(). But if a process contains multiple database connections each can choose a different VFS. VFSes can be added at run-time using the sqlite3_vfs_register() interface.

在Unix、Windows和OS/2上构建的SQLite都默认包含了适合目标平台的VFS。在其它平台上构建的SQLite则默认不包含VFS,不过应用程序可以再运行时注册一个或多个VFS。
The default builds for SQLite on Unix, Windows, and OS/2 include a VFS appropriate for the target platform. SQLite builds for other operating systems do not contain a VFS by default, but the application can register one or more at run-time.

5.0 将SQLite移植到新操作系统中
5.0 Porting SQLite To A New Operating System

为了将SQLite移植到新的操作系统中(一个默认不支持的系统),应用程序需要提供:
In order to port SQLite to a new operating system - an operating system not supported by default - the application must provide...

所有的这些内容都可以在一个辅助的C源码文件中提供,并将其与已有的“sqlite3.c”源码文件链接在一起来生成一个在目标操作系统上可以运行的SQLite版本。除了互斥锁和内存分配子系统以及新的VFS以外,这个辅助C源码文件还需要包含下面两个接口的实现:
All of these things can be provided in a single auxiliary C code file and then linked with the stock "sqlite3.c" code file to generate a working SQLite build for the target operating system. In addition to the alternative mutex and memory allocation subsystems and the new VFS, the auxiliary C code file should contain implementations for the following two routines:

“sqlite3.c”源文件中包含了默认的VFS实现,以及适合Unix、Windows和OS/2的sqlite3_initialize()sqlite3_shutdown()函数。要想在编译sqlite3.c时防止加载这些默认的组件,就需要添加一个编译期选项:
The "sqlite3.c" code file contains default implementations of a VFS and of the sqlite3_initialize() and sqlite3_shutdown() functions that are appropriate for Unix, Windows, and OS/2. To prevent one of these default components from being loaded when sqlite3.c is compiled, it is necessary to add the following compile-time option:

-DSQLITE_OS_OTHER=1

SQLite内核会在提早调用sqlite3_initialize()。辅助C源文件可以包含sqlite3_initialize()的实现,这个函数可以注册合适的VFS,也可以初始化互斥锁系统(如果需要互斥锁)或者内存分配子系统所需的初始化工作。 SQLite核心永远不会调用sqlite3_shutdown(),但是这是官方SQLite API的一部分,必须在使用-DSQLITE_OS_OTHER=1编译的时候提供。所以辅助C源文件需要提供这个函数。
The SQLite core will call sqlite3_initialize() early. The auxiliary C code file can contain an implementation of sqlite3_initialize() that registers an appropriate VFS and also perhaps initializes an alternative mutex system (if mutexes are required) or does any memory allocation subsystem initialization that is required. The SQLite core never calls sqlite3_shutdown() but it is part of the official SQLite API and is not otherwise provided when compiled with -DSQLITE_OS_OTHER=1, so the auxiliary C code file should probably provide it for completeness.