分享一下我老师大神的人工智能教程。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow
在Chromium中,每一个Plugin都对应一个Module,称为Plugin Module。一个Plugin Module可创建多个Plugin Instance。每一个Plugin Instance对应于网页中的一个<embed>标签。在为<embed>标签创建Plugin Instance之前,先要加载其对应的Plugin Module。本文接下来分析Plugin Module的加载过程。
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!
Plugin Module的加载过程如图1所示:
图1 Plugin Module的加载过程
WebKit请求Content层为网页中的<embed>标签创建Plugin Instance时,Content层会检查要创建的Plugin Instance对应的Plugin Module是否已经加载。如果还没有加载,那么通常就会创建一个Out-of-Process Plugin Module。这里说通常,是因为大部分Plugin都是运行在一个独立的进程中,只有内置Plugin才允许运行在Render进程中。本文我们只讨论Out-of-Process Plugin Module的情形。
Content层在创建Out-of-Process Plugin Module的过程中,会请求Browser进程创建一个Plugin进程,并且请求该Plugin进程加载指定的Plugin Module。每一个Plugin Module都会有一个导出函数PPP_InitializeModule。Plugin Module加载完成之后,它导出的函数PPP_InitializeModule就会被调用,用来执行初始化工作。
Plugin进程启动起来之后,它与Render进程之间的通信是通过一个PluginDispatcher对象进行的。与此同时,Render进程也会通过一个HostDispatcher对象与Plugin进程进行通信。例如,Content层请求Plugin进程加载指定的Plugin Module,就是通过Render进程中的HostDispatcher对象向Plugin进程中的PluginDispatcher对象发送IPC消息进行的。
Content层在请求Browser进程创建一个Plugin进程加载一个Plugin Module之前,必须要知道这个Plugin Module的信息,例如它的SO文件路径。用户当前安装的所有Plugin是由Chromium中的一个Plugin Service进行管理的,因此Content层可以通过这个Plugin Service获得要加载的Plugin Module的信息。
在分析Plugin Module的加载之前,我们先分析Plugin Service的启动过程。在启动的过程中,它就会注册所有内建(Built-In)的Plugin以及用户安装的Plugin在内部的一个List中。从前面Chromium扩展(Extension)加载过程分析一文可以知道,Chromium的Browser进程在启动的时候,会调用BrowserMainLoop类的成员函数CreateStartupTasks创建一系列的Startup Task。其中的一个Startup Task就是用来启动Plugin Service的,如下所示:
void BrowserMainLoop::CreateStartupTasks() { ...... // First time through, we really want to create all the tasks if (!startup_task_runner_.get()) {
#if defined(OS_ANDROID) startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner( base::Bind(&BrowserStartupComplete), base::MessageLoop::current()->message_loop_proxy()));#else startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner( base::Callback<void(int)>(), base::MessageLoop::current()->message_loop_proxy()));#endif StartupTask pre_create_threads = base::Bind(&BrowserMainLoop::PreCreateThreads, base::Unretained(this)); startup_task_runner_->AddTask(pre_create_threads); ...... }#if defined(OS_ANDROID) if (!BrowserMayStartAsynchronously()) { // A second request for asynchronous startup can be ignored, so // StartupRunningTasksAsync is only called first time through. If, however, // this is a request for synchronous startup then it must override any // previous call for async startup, so we call RunAllTasksNow() // unconditionally. startup_task_runner_->RunAllTasksNow(); }#else startup_task_runner_->RunAllTasksNow();#endif}
这个函数定义在文件external/chromium_org/content/browser/browser_main_loop.cc中。
这个Startup Task绑定了BrowserMainLoop类的成员函数PreCreateThreads,在创建各种Browser线程之前执行,执行过程如下所示:
int BrowserMainLoop::PreCreateThreads() { ......#if defined(ENABLE_PLUGINS) // Prior to any processing happening on the io thread, we create the // plugin service as it is predominantly used from the io thread, // but must be created on the main thread. The service ctor is // inexpensive and does not invoke the io_thread() accessor. { TRACE_EVENT0("startup", "BrowserMainLoop::CreateThreads:PluginService"); PluginService::GetInstance()->Init(); }#endif ...... return result_code_;}
这个函数定义在文件external/chromium_org/content/browser/browser_main_loop.cc中。
从这里可以看到,只有在编译时定义了宏ENABLE_PLUGINS,Chromium才会支持Plugin机制。在这种情况下,BrowserMainLoop类的成员函数PreCreateThreads首先会调用PluginService类的静态成员函数GetInstance获得一个PluginServiceImpl对象,如下所示:
PluginService* PluginService::GetInstance() { return PluginServiceImpl::GetInstance();}
这个函数定义在文件external/chromium_org/content/browser/plugin_service_impl.cc中。
PluginService的静态成员函数GetInstance又是通过调用PluginServiceImpl类的静态成员函数GetInstance获得一个PluginServiceImpl对象的,如下所示:
PluginServiceImpl* PluginServiceImpl::GetInstance() { return Singleton<PluginServiceImpl>::get();}
这个函数定义在文件external/chromium_org/content/browser/plugin_service_impl.cc中。
从这里可以看到,PluginServiceImpl类的静态成员函数GetInstance返回的PluginServiceImpl对象在当前进程(即Browser进程)是唯一的。这个PluginServiceImpl对象返回给BrowserMainLoop类的成员函数PreCreateThreads之后,后者会调用它的成员函数Init执行初始化工作,如下所示:
void PluginServiceImpl::Init() { ...... RegisterPepperPlugins(); ......}
这个函数定义在文件external/chromium_org/content/browser/plugin_service_impl.cc中。
PluginServiceImpl类的成员函数Init会调用另外一个成员函数RegisterPepperPlugins注册那些Built-In Plugin以及用户安装的Plugin到Plugin Service中去,如下所示:
void PluginServiceImpl::RegisterPepperPlugins() { ComputePepperPluginList(&ppapi_plugins_); for (size_t i = 0; i < ppapi_plugins_.size(); ++i) { RegisterInternalPlugin(ppapi_plugins_[i].ToWebPluginInfo(), true); }}
这个函数定义在文件external/chromium_org/content/browser/plugin_service_impl.cc中。
PluginServiceImpl类的成员函数RegisterPepperPlugins首先调用函数ComputePepperPluginList获得那些Built-In Plugin和用户安装的Plugin,如下所示:
void ComputePepperPluginList(std::vector<PepperPluginInfo>* plugins) { GetContentClient()->AddPepperPlugins(plugins); ComputePluginsFromCommandLine(plugins);}
这个函数定义在文件external/chromium_org/content/common/pepper_plugin_list.cc中。
这里我们假设当前分析的是Chrome浏览器。在这种情况下,PluginServiceImpl类的成员函数RegisterPepperPlugins调用函数GetContentClient获得的是一个ChromeContentClient对象。调用这个ChromeContentClient对象的成员函数AddPepperPlugin即可以获得Built-In Plugin,如下所示:
void ChromeContentClient::AddPepperPlugins( std::vector<content::PepperPluginInfo>* plugins) { ComputeBuiltInPlugins(plugins); AddPepperFlashFromCommandLine(plugins); content::PepperPluginInfo plugin; if (GetBundledPepperFlash(&plugin)) plugins->push_back(plugin);}
这个函数定义在文件external/chromium_org/chrome/common/chrome_content_client.cc中。
ChromeContentClient类的成员函数AddPepperPlugin首先调用函数ComputeBuiltInPlugins获得一些重要的Built-In Plugin,例如NaCl Plugin和PDF Plugin,如下所示:
void ComputeBuiltInPlugins(std::vector<content::PepperPluginInfo>* plugins) { // PDF. // // Once we're sandboxed, we can't know if the PDF plugin is available or not; // but (on Linux) this function is always called once before we're sandboxed. // So the first time through test if the file is available and then skip the // check on subsequent calls if yes. static bool skip_pdf_file_check = false; base::FilePath path; if (PathService::Get(chrome::FILE_PDF_PLUGIN, &path)) { if (skip_pdf_file_check || base::PathExists(path)) { content::PepperPluginInfo pdf; pdf.path = path; pdf.name = ChromeContentClient::kPDFPluginName; if (CommandLine::ForCurrentProcess()->HasSwitch( switches::kOutOfProcessPdf)) { pdf.is_out_of_process = true; content::WebPluginMimeType pdf_mime_type(kPDFPluginOutOfProcessMimeType, kPDFPluginExtension, kPDFPlugi