SQLite剖析(2):编译及应用

    若要编译SQLite库,需要下载单一文件版本sqlite-amalgamation-3071400.zip。它把sqlite3库的所有源码文件内容放到一个文件sqlite3.c中了(不包含管理工具shell.c),这个文件大概有110000多行,如果除去空白行和注释,则有65000多行的代码!这样做的好处是很容易应用在你的项目中,只需拷贝这一个源文件到你项目中即可。另外,编译器在编译单一的文件时能做一些额外的优化,因为只有一个编译单元。通过测试发现大概有5%-10%的性能提升。
    SQLite库可以不编译,直接把单一文件sqlite3.c(或者再加上sqlite3.h)拷贝到你的项目中使用即可。但作为一个单独的库,一般建议编译成独立的二进制文件格式的库,如Linux下的.so动态链接库,Windows下的DLL动态链接库,然后在项目中通过头文件sqlite3.h来使用这个库,这样能使软件更加地模块化。
     1、Linux下编译SQLite
    (1)编译命令行管理工具:gcc shell.c sqlite3.c -lpthread -ldl -o sqlite3
    将生成sqlit3命令行管理工具。
    (2)编译SQLite为单独的动态链接库:gcc sqlite3.c -lpthread -ldl -fPIC -shared -o libsqlite3.so    
    -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
    -shared:表示生成一个共享目标文件(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),即我们所说的动态链接库。它可以和其他目标文件连接产生可执行文件。只有部分系统支持该选项。
    pthread系统库用于确保SQLite是线程安全的。但因为命令行工具是单线程的,对命令行工具则可编译成非线程安全的,以忽略pthread库。命令为gcc -DSQLITE_THREADSAFE=0 shell.c sqlite3.c -ldl -o sqlite3。dl系统库用于支持动态装载,sqlite3_load_extension()接口和SQL函数load_extension()需要用到它。如果不需要这些特性,可以使用SQLITE_OMIT_LOAD_EXTENSION编译选项来忽略,如gcc -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION shell.c sqlite3.c -o sqlite3。
    使用动态库libsqlite3.so:在你的程序中(例如test.c)通过包含头文件sqlite3.h来使用库中的函数,编译程序的命令为gcc test.c -L. -lsqlite3 -o test。其中-L.表示让链接库的搜索路径包含当前目录,-lsqlite3指明编译器查找动态库libsqlite3.so,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称。通过ldd test可查看test程序是如何调用动态库中的函数的。
    调用动态库时有几个问题会经常碰到。有时明明已经将库的头文件所在目录通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是修改LD_LIBRARY_PATH,这个环境变量指示动态连接器可以装载动态库的路径。或者修改/etc/ld.so.conf文件,然后调用/sbin/ldconfig来达到同样的目的。通常这样做就可以解决库无法链接的问题了。
    (3)编译成静态库:gcc -c sqlite3.c -lpthread -ldl -o sqlite3.o编译成目标文件,ar -r libsqlite3.a sqlite3.o将列出的各个目标文件一起打包成一个静态库libsqlite3.a。
    链接静态库:gcc test.c -L. -lsqlite3 -static -o test。也可不加-static选项。
     2、Windows下编译SQLite
    (1)把SQLite编译成动态链接库:
    打开Visual Studio 2010,新建一个名为sqlite3的Visual C++ Win32工程,在工程向导页中选择工程的类型为 "DLL", 并且把创建为空项目的复选框钩上。通过工程--->添加现有项...,把单一文件sqlite3.c添加到工程中。为了生成在链接sqlite3.dll时需要用到的lib文件,需要在工程中添加模块定义文件。根据sqlite3.h中列出的导出函数名,我们可以自己写.def文件,例如:
EXPORTS
sqlite3_aggregate_context
sqlite3_aggregate_count
sqlite3_auto_extension
sqlite3_backup_finish
sqlite3_backup_init
sqlite3_backup_pagecount
sqlite3_backup_remaining
sqlite3_backup_step
sqlite3_bind_blob
sqlite3_bind_double
;......
    也可以使用已写好的.def文件,下载已编译好的SQLite DLL库sqlite-dll-win32-x86-3071400.zip,里面有sqlite3.def,把它拷贝到我们的sqlite3项目中,在工程属性的Linker--->Input--->Module Definition File中输入sqlite3.def。设置项目编译成Release版本,编译后生成sqlite3.dll和sqlite3.lib。
    编译命令行管理工具:如果想编译出sqlite.exe命令行程序,则需要创建一个空的Win32 控制台程序,然后在往工程里添加文件sqlite3.c和shell.c,直接编译即可。
    使用动态链接库sqlite3.dll:

    本例子整理自http://sqlite.org/quickstart.html。新建一个空项目test,把sqlite3.dll, sqlite3.lib, sqlite3.h拷贝到项目目录下,把sqlite3.h添加到项目中。新建主程序源文件test.cpp,如下:

[cpp] view plain copy print ?
  1. #include <stdio.h>   
  2. #include "sqlite3.h"   
  3. #pragma comment(lib,"sqlite3")  
  4.   
  5. /* print a record from table outputed by sql statement */  
  6. static int callback(void *NotUsed, int argc, char **argv, char **azColName){  
  7.     int i;  
  8.     for(i=0; i<argc; i++){  
  9.         printf("%s = %s\n", azColName[i], argv[i]?argv[i]:"NULL");  
  10.     }  
  11.     printf("\n");  
  12.     return 0;  
  13. }  
  14.   
  15. int main(int argc, char **argv){  
  16.     sqlite3 *db;  
  17.     char *zErrMsg=0;  
  18.     int rc;  
  19.   
  20.     if(argc!=3){  
  21.         fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);  
  22.         return(1);  
  23.     }  
  24.     rc=sqlite3_open(argv[1],&db);  /* open database */  
  25.     if(rc){  
  26.         fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));  
  27.         sqlite3_close(db);  
  28.         return(1);  
  29.     }  
  30.     rc=sqlite3_exec(db,argv[2],callback,0,&zErrMsg);  /* execute SQL statement */  
  31.     if(rc!=SQLITE_OK){  
  32.         fprintf(stderr,"SQL error: %s\n",zErrMsg);  
  33.         sqlite3_free(zErrMsg);  
  34.     }  
  35.     sqlite3_close(db);  /* close database */  
  36.     return 0;  
  37. }  
    编译后生成test.exe程序,它的运行依赖于sqlite3.dll。注意程序中也可以不使用pragma指令导入sqlite3.lib,而是在test项目属性中添加对上面的dll项目sqlit3的引用。
    这个C程序的例子显示怎么使用sqlite的C/C++接口。数据库的名字由第一个参数取得,第二个参数是一条或更多的SQL执行语句。这个程序调用sqlite3_open()打开指定的数据库,调用sqlite3_exec()对数据库执行SQL语句,callback函数会作用在SQL语句结果集的每条记录上。最后用sqlite3_close()关闭数据库连接。可以用前面的alf.db数据库来测试:test.exe alf.db "select * from mytable"。
    (2)把SQLite编译成静态链接库:
    新建一个名为sqlite3static,空的Visual C++ Win32工程,在工程向导页中选择工程的类型为 "Static library",去年"Precompiled header"复选框,导入源文件sqlite3.c,编译生成sqlite3static.lib,这是静态链接库版本,它要比动态链接库sqlite3.dll大的多。
    使用静态链接库:
    新建一个空项目sqlite3statictest,添加对项目sqlite3static的引用,同样使用上面的test.cpp,编译生成sqlite3statictest.exe。可以发现它是一个独立的可执行文件,不依赖静态链接库,删除静态链接库后仍然可以运行。
    实际上,静态链接库不存在导出的概念,在链接过程中,静态链接库LIB中的指令都全部被直接包含在最终生成的EXE文件中了,因此如果用的是静态链接库,那么也就不存在“导出某个函数提供给用户使用”的情况。对于使用该静态库的EXE文件来说,直接使用LIB中的函数和全局变量即可,而不管它们有没有导出声明。另外一点在使用中需要注意的是:静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含。
     3、多线程环境中使用SQLite
    内容整理自http://sqlite.org/threadsafe.html。SQLite支持三种不同的线程模式:
    * 单线程。在这种模式下所有的互斥锁都被禁用,在多个线程中同时使用SQLite时是不安全的。
    * 多线程。在这种模式下,只要没有单个数据库连接被同时用在多个线程中的情况,SQLite就可以在多线程环境中安全地使用。
    * 串行化。在这种模式中,SQLite可以无限制地在多线程环境中安全地使用。
    线程模式可以在编译时(把SQLite源代码编译成库时)、启动时(使用SQLite的应用程序初始化时)或运行时(一个新的SQLite数据库连接创建时)指定。一般来说,运行时参数会覆盖掉启动时参数,启动时参数会覆盖掉编译时参数,但是单线程模式一旦被指定后,就不能被覆盖。默认的模式是串行模式。
    (1)编译时的线程模式选项
    通过SQLITE_THREADSAFE编译时参数来选择线程模式。如果没有指定SQLITE_THREADSAFE参数,则使用串行化模式。也可以显式地使用-DSQLITE_THREADSAFE=1来指定串行化模式。-DSQLITE_THREADSAFE=0表示单线程模式,-DSQLITE_THREADSAFE=2表示多线程模式。
    sqlite3_threadsafe()接口的返回值由编译时线程模式选项来确定。如果编译时指定单线程模式,则sqlite3_threadsafe()返回false。如果指定多线程模式或串行化模式,则sqlite3_threadsafe()返回true。sqlite3_threadsafe()不能区分多线程模式和串行化模式,也不能报告启动时或运行时的模式更改。
    如果编译时指定单线程模式,则编译库时关键的互斥逻辑会被忽略,因此不可能在启动时或运行不可能再激活多线程或串行化模式。
    (2)启动时的线程模式选项
    如果编译时没有指定单线程模式,则在使用sqlite3_config()接口进行初始化时可以改变线程模式。SQLITE_CONFIG_SINGLETHREAD谓词把SQLite设置成单线程模式,SQLITE_CONFIG_MULTITHREAD设置多线程模式,SQLITE_CONFIG_SERIALIZED设置串行化模式。
    (3)运行时的线程模式选项
    如果编译时或启动时没有指定单线程模式,则单个数据连接可以被创建为多线程或串行化模式,不可能将单个数据库连接降级为单线程模式。如果编译时或启动时指定单线程模式,则不可能将单个数据库连接升级为多线程或串行化模式。
    单个数据库连接的线程模式由sqlite3_open_v2()的第三个参数给定的标志来确定。SQLITE_OPEN_NOMUTEX标志表示数据库连接为多线程模式,SQLITE_OPEN_FULLMUTEX表示该连接为串行化模式。如果没有指定标志,或者使用sqlite3_open(), sqlite3_open16(),而不是sqlite3_open_v2(),则使用编译时或启动时指定的线程模式。

你可能感兴趣的:(SQLite剖析(2):编译及应用)