Qt5的插件机制(3)--QLibraryPrivate类与QLibraryStore类

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

QLibraryPrivate 类

QLibraryPrivate 类的重要性

Qt中能加载库或插件的几个类:
 
    QLibrary ,
    QPluginLoader ,
    QFactoryLoader ,
    QStaticPlugin (暂时不研究这个)
    
QLibrary 和 QPluginLoader 依赖的'私有数据类'都是 QLibraryPrivate, 一个QLibrary或QPluginLoader的对象都有一个QLibraryPrivate对象,对应一个库或插件;
QFactoryLoader 依赖的'私有数据类'是 QFactoryLoaderPrivate , 但 QFactoryLoaderPrivate 类中又包含了一个QLibraryPrivate列表,这个列表中有多个
QLibraryPrivate类型的元素,对应一系列的库或插件;
所以可见,QLibraryPrivate是Qt中与库或插件相关的核心数据类,每个库都对应一个QLibraryPrivate对象。

QLibraryPrivate类的一些重要成员如下:
[cpp] view plain
  1. class QLibraryPrivate  
  2. {  
  3. public:  
  4. #ifdef Q_OS_WIN  
  5.     HINSTANCE  
  6. #else  
  7.     void *  
  8. #endif  
  9.     pHnd;    // 库的句柄。当库被加载load成功后,会被设置为有效值;否则,是Null  
  10.   
  11.     QString fileName, qualifiedFileName;    // 库文件名  
  12.     QString fullVersion;  
  13.   
  14.     bool load();        // 加载库,内部通过与系统相关的函数 load_sys() 实现  
  15.     bool loadPlugin(); // loads and resolves instance    // 内部会调用load,然后设置 instance 函数  
  16.     bool unload(UnloadFlag flag = UnloadSys);    // 卸载库,内部通过与系统相关的函数 unload_sys() 实现  
  17.     void release();        // 释放库,内部会调用 类QLibraryStore 的 releaseLibrary() 方法  
  18.     QFunctionPointer resolve(const char *);    // 将库中的符号实例化,内部通过与系统相关的函数 resolve_sys() 实现  
  19.   
  20.     static QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version = QString(),  
  21.                                          QLibrary::LoadHints loadHints = 0);  
  22.     ...  
  23.     QtPluginInstanceFunction instance;      
  24.         // instance是产生插件实例的函数,返回QObject*。这个函数在构造函数中被初始化为NULL。  
  25.         // 插播一句,在Qt编译出来的插件库文件(xxx.so)中,用 "nm xxx.so -D" 命令列出其中的符号,  
  26.         // 可以看到其中有 qt_plugin_instance 和 qt_plugin_query_metadata 符号,这两个符号  
  27.         // 都是 Qt 在编译插件时由MOC系统将Q_PLUGIN_METADATA 宏展开后生成的,他们是两个函数,  
  28.         // 其中qt_plugin_instance 函数会在插件加载load()成功后,赋值给 QLibraryPrivate类的instance(这一步是在 loadPlugin() 函数中实现的),  
  29.         // 这样就能直接通过 QLibraryPrivate::instance() 访问到插件的实例了。  
  30.         // 而另一个函数qt_plugin_query_metadata则用于获取插件的元信息metaData  
  31.     QPointer<QObject> inst;    // 插件的实例,只有当这个QLibraryPrivate对象属于一个QPluginLoader对象时,QPluginLoader::instance()函数中  
  32.                 // 才会设置这个 inst, 参见下面插入的几行代码。另外,在 unload 函数中,inst 会被delete掉  
  33.                 /*    QObject *QPluginLoader::instance() 
  34.                     { 
  35.                         if (!isLoaded() && !load()) 
  36.                         return 0; 
  37.                         if (!d->inst && d->instance) 
  38.                         d->inst = d->instance();    // 这一步设置 inst 
  39.                         return d->inst.data(); 
  40.                     }        */  
  41.   
  42.   
  43.     QJsonObject metaData;    // 库的元信息  
  44.     ...  
  45.     QLibrary::LoadHints loadHints;    // 加载策略  
  46.   
  47.     void updatePluginState();    // 更新库的插件状态,内部会设置下面的成员变量 pluginState  
  48.     bool isPlugin();        // 调查这个库是不是插件,内部会读取成员变量 pluginState  
  49.   
  50.     static inline QJsonDocument fromRawMetaData(const char *raw) {        // 从二进制数据中获取QJson。插件/库的元数据就从此而来  
  51.         raw += strlen("QTMETADATA  ");  
  52.         // the size of the embedded JSON object can be found 8 bytes into the data (see qjson_p.h),  
  53.         // but doesn't include the size of the header (8 bytes)  
  54.         QByteArray json(raw, qFromLittleEndian<uint>(*(uint *)(raw + 8)) + 8);  
  55.         return QJsonDocument::fromBinaryData(json);  
  56.     }  
  57.   
  58. private:  
  59.     explicit QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints);  
  60.     ~QLibraryPrivate();    // 构造与析构  
  61.     void mergeLoadHints(QLibrary::LoadHints loadHints);    // 设置加载策略  
  62.   
  63.     bool load_sys();    // 与系统相关的加载、卸载或 resolve函数,内部会调用系统API  
  64.     bool unload_sys();  
  65.     QFunctionPointer resolve_sys(const char *);  
  66.   
  67.   
  68.     /// counts how many QLibrary or QPluginLoader are attached to us, plus 1 if it's loaded  
  69.     QAtomicInt libraryRefCount;            // 引用计数  
  70.     /// counts how many times load() or loadPlugin() were called  
  71.     QAtomicInt libraryUnloadCount;        // 加载计数。虽然从变量名来看是卸载计数,但从注释中可以看出是加载计数  
  72.   
  73.     enum { IsAPlugin, IsNotAPlugin, MightBeAPlugin } pluginState;    // 标记这个库是不是一个插件,构造函数中初始化为 MightBeAPlugin,即不确定状态。  
  74.     friend class QLibraryStore;  
  75. };  

构造/析构类函数定义如下。其中构造函数中的参数 canonicalFileName是库的标准文件名,version是版本号,loadHints用于控制库加载
时的动作,比如在加载该库时而不是等到调用resolve时,就将库中的所有符号实例化(ResolveAllSymbolsHint)等。
构造函数只检查库文件名是否有效,析构函数中则什么也不做。findOrCreate函数是个静态函数,它能返回一个QLibraryPrivate指针,其作用与
QLibraryStore类的findOrCreate函数一致(本文后面有对QLibraryStore类及其findOrCreate函数的介绍)

[cpp] view plain
  1. QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints)  
  2.     : pHnd(0), fileName(canonicalFileName), fullVersion(version), instance(0),  
  3.       loadHints(loadHints),  
  4.       libraryRefCount(0), libraryUnloadCount(0), pluginState(MightBeAPlugin)  
  5. {  
  6.     if (canonicalFileName.isEmpty())  
  7.         errorString = QLibrary::tr("The shared library was not found.");  
  8. }  
  9.   
  10. QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version,  
  11.                                                QLibrary::LoadHints loadHints)  
  12. {  
  13.     return QLibraryStore::findOrCreate(fileName, version, loadHints);  
  14. }  
  15.   
  16. QLibraryPrivate::~QLibraryPrivate()  
  17. {  
  18. }  



QLibraryPrivate类中需要我们格外关心的一个成员变量是 metaData, 其类型为 QJsonObject , 它描述了一系列与某个
库相关的元信息 , 元信息中包含有库或Qt插件的IDD、关键字Keys等重要信息。与元信息有关的内容在另外一片文章中单独介绍。


<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

QLibraryStore (库商店)类


先看QLibraryStore类的定义

[cpp] view plain
  1. QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints)  
  2.     : pHnd(0), fileName(canonicalFileName), fullVersion(version), instance(0),  
  3.       loadHints(loadHints),  
  4.       libraryRefCount(0), libraryUnloadCount(0), pluginState(MightBeAPlugin)  
  5. {  
  6.     if (canonicalFileName.isEmpty())  
  7.         errorString = QLibrary::tr("The shared library was not found.");  
  8. }  
  9.   
  10. QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version,  
  11.                                                QLibrary::LoadHints loadHints)  
  12. {  
  13.     return QLibraryStore::findOrCreate(fileName, version, loadHints);  
  14. }  
  15.   
  16. QLibraryPrivate::~QLibraryPrivate()  
  17. {  
  18. }  


注意一点:这个类没有多少干货,它所有的方法都是静态的! 只有一个非静态的实体成员 libraryMap 。
libraryMap是个QMap类型的映射表,它保存了从一个QString列表到一个QLibraryPrivate*列表的映射。其中,QString列表中是所有的库对应的库文件名,而
QLibraryPrivate列表中则是所有的库对应的QLibraryPrivate对象。除了这个列表外,QLibraryStore这个类没有任何其他
实体成员。在一个Qt应用程序或进程中,一般只需要一个这样的映射表,所以也就只需要一个 QLibraryStore 的实例,Qt 内部也确实是这么做
的,这可以从QLibrary.cpp文件中看出来 (QLibraryStore这个类的方法的具体定义基本都在QLibrary.cpp文件中):

首先,该文件中有如下几个全局静态变量:

[cpp] view plain
  1. static QLibraryStore *qt_library_data = 0;      
  2. static bool qt_library_data_once;      

其中qt_library_data就是指向QLibraryStore类的一个静态全局实例的指针,外部通过调用QLibraryStore类的静态
方法 instance() 来访问它。而变量qt_library_data_once则用来标记qt_library_data是否曾被创建过。
这个方法定义如下:

[cpp] view plain
  1. QLibraryStore *QLibraryStore::instance()  
  2. {  
  3.     if (Q_UNLIKELY(!qt_library_data_once && !qt_library_data)) {  
  4.         // only create once per process lifetime  每个进程的生命周期中只创建一次QLibraryStore  
  5.         qt_library_data = new QLibraryStore;  
  6.         qt_library_data_once = true;  
  7.     }  
  8.     return qt_library_data;  
  9. }  
  10. QLibraryStore::~QLibraryStore()  
  11. {  
  12.     qt_library_data = 0;    // 这个类的析构函数只是清除一个全局指针,从这里也反映出这个类就是为专门全局静态变量qt_library_data准备的  
  13. }  

注意,qt_library_data虽然被初始化为了NULL,但当外部第一次调用QLibraryStore类的instance方法时,qt_library_data就变成有效指针了。


findOrCreate 方法用于从 QLibraryStore 的映射表中查找是否存在库文件名是fileName的库,如果存在则直接返回该库对应的QLibraryPrivate对象,
如果映射表中不存在,则new一个QLibraryPrivate对象并将其添加到映射表中。使用这个函数来获取 QLibraryPrivate 对象可以保证不会出现多个
QLibraryPrivate 对象映射到同一个库文件上的现象。

[cpp] view plain
  1. inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, const QString &version,  
  2.                                                     QLibrary::LoadHints loadHints)  
  3. {  
  4.     QMutexLocker locker(&qt_library_mutex);  
  5.     QLibraryStore *data = instance();  
  6.   
  7.     // check if this library is already loaded  
  8.     QLibraryPrivate *lib = 0;  
  9.     if (Q_LIKELY(data)) {  
  10.         lib = data->libraryMap.value(fileName);  
  11.         if (lib)  
  12.             lib->mergeLoadHints(loadHints);  
  13.     }  
  14.     if (!lib)  
  15.         lib = new QLibraryPrivate(fileName, version, loadHints);  
  16.   
  17.     // track this library  
  18.     if (Q_LIKELY(data))  
  19.         data->libraryMap.insert(fileName, lib);  
  20.   
  21.     lib->libraryRefCount.ref();    // 库的引用计数加 1  
  22.     return lib;  
  23. }  


与findOrCreate相对的函数是releaseLibrary,他负责释放一个库。但这个函数不一定会释放指定的库,而是先将库的引用计数减 1 ,只有引用计数变为0时才释放。
[cpp] view plain
  1. inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib)  
  2. {  
  3.     QMutexLocker locker(&qt_library_mutex);  
  4.     QLibraryStore *data = instance();  
  5.   
  6.     if (lib->libraryRefCount.deref()) {  
  7.         // still in use  
  8.         return;  
  9.     }  
  10.     // no one else is using  
  11.     Q_ASSERT(lib->libraryUnloadCount.load() == 0);  
  12.   
  13.     if (Q_LIKELY(data)) {  
  14.         QLibraryPrivate *that = data->libraryMap.take(lib->fileName);  
  15.         Q_ASSERT(lib == that);  
  16.         Q_UNUSED(that);  
  17.     }  
  18.     delete lib;        // QLibraryPrivate的析构函数中什么也不做  
  19. }  



QLibraryStore类的cleanup函数负责卸载(unload)所有不再使用的库,并释放映射表中所有的 QLibraryPrivate 对象

[cpp] view plain
  1. inline void QLibraryStore::cleanup()  
  2. {  
  3.     QLibraryStore *data = qt_library_data;  
  4.     if (!data)  
  5.         return;  
  6.   
  7.     // find any libraries that are still loaded but have a no one attached to them  
  8.     LibraryMap::Iterator it = data->libraryMap.begin();  
  9.     for (; it != data->libraryMap.end(); ++it) {  
  10.         QLibraryPrivate *lib = it.value();  
  11.         if (lib->libraryRefCount.load() == 1) {  
  12.             if (lib->libraryUnloadCount.load() > 0) {  
  13.                 Q_ASSERT(lib->pHnd);  
  14.                 lib->libraryUnloadCount.store(1);  
  15. #ifdef __GLIBC__  
  16.                 // glibc has a bug in unloading from global destructors  
  17.                 // see https://bugzilla.novell.com/show_bug.cgi?id=622977  
  18.                 // and http://sourceware.org/bugzilla/show_bug.cgi?id=11941  
  19.                 lib->unload(QLibraryPrivate::NoUnloadSys);  
  20. #else  
  21.                 lib->unload();  
  22. #endif  
  23.             }  
  24.             delete lib;  
  25.             it.value() = 0;      
  26.         }  
  27.     }  
  28.   
  29.     if (qt_debug_component()) {  
  30.         // dump all objects that remain  在调试模式下,如果有泄露的库,则一一显示  
  31.         foreach (QLibraryPrivate *lib, data->libraryMap) {  
  32.             if (lib)  
  33.                 qDebug() << "On QtCore unload," << lib->fileName << "was leaked, with"  
  34.                          << lib->libraryRefCount.load() << "users";  
  35.         }  
  36.     }  
  37.   
  38.     delete data;  

你可能感兴趣的:(Qt5的插件机制(3)--QLibraryPrivate类与QLibraryStore类)