CEF3多进程模型
browser进程:主进程browser进程处理窗口创建、窗口绘制、网络交互以及大部分的主要逻辑。browser进程通常就是宿主进程。
render进程:blink的渲染和js的执行,通常在分离的render(渲染)进程里。除此之外,render进程还会处理一些逻辑,例如js bindings和dom节点的访问。进程模型默认会为每个唯一源(协议+域)创建一个新的render进程。
下面尝试了变换新的域名发现(换协议没试),一个browser加载一个新域名的url,会将原有render进程杀掉,创建新的render进程。首先在demo中加载了CSDN首页,发现应用程序中有三个进程PID 85320为browser通常就是宿主进程,PID 85588为gpu加速进程,PID 76600为render进程,如下图:
然后加载百度的首页,发现browser进程与gpu进程没有发生改变,CSDN的render进程被杀死,cef新创建一个baidu的render进程,PID为87952,如下图:
若源(协议+域)不变则render进程不会改变。
其他进程:例如一些插件像flash进程以及处理合成加速的gpu进程等都是按需创建。
cef3可以通过IPC进行进程间通信,browser和render进程可以通过发送异步消息进行双向通信。
执行体
主应用执行体通常会多次启动独立的子进程。这是通过传递不同的命令行参数给CefExecuteProcess 函数实现的。如果主应用执行体比较复杂,加载时间花费比较长,宿主程序可以使用分离的执行体去运行这些进程。这种方式是通过初始化cef时设置CefSettings.browser_subprocess_path参数实现的。
在可执行程序入口点调用CefExecuteProcess函数,开始执行子进程。该函数也可在分离执行体中调用。若在browser进程调用会立即返回-1,在分离执行体中调用会阻塞并在进程退出时返回退出码。下面在windowsQt环境下说明这两种方式的区别:
单一执行体
void print(const QString &cmd)
{
QFile file("E://aaa.txt");
QString data = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz ") + cmd + "\n";
file.open(QIODevice::Append);
file.write(data.toUtf8());
file.close();
}
int main(int argc, char *argv[])
{
Application a(argc, argv);
//命令行参数结构体
//这个结构体的定义与平台相关
CefMainArgs main_args(GetModuleHandle(NULL));
//CefApp接口
CefRefPtr<BrowserApp> app(new BrowserApp);
//打印进程命令行
CefRefPtr<CefCommandLine> commandLine = CefCommandLine::CreateCommandLine();
commandLine->InitFromString(::GetCommandLineW());
QString cmd = QString::fromStdString(commandLine->GetCommandLineString().ToString());
print(cmd);
//单一执行体,执行子进程
int exit_code = CefExecuteProcess(main_args, app, NULL);
if (exit_code >= 0) {
return -1;
}
//设置该结构体,cef初始化时定制行为
CefSettings settings;
//开启事件循环
settings.multi_threaded_message_loop = true;
//分离执行体
//QString render = "CEFRender.exe";
//CefString(&settings.browser_subprocess_path).FromString(render.toStdString());
//在主进程初始化cef
CefInitialize(main_args, settings, app, NULL);
return a.exec();
}
在CefExecuteProcess函数前打印了命令行结果如下:
由打印结果可知,单一执行体所有开启的进程都会从该执行体的入口点函数(本例为main函数)开始执行。Browser进程执行CefExecuteProcess会立即返回-1,其他进程会阻塞在该函数直到进程退出。也就是说CefExecuteProcess之前的全部代码都会执行。因此,若执行体比较复杂,启动子进程的速度会很慢,也会执行子进程不必要的代码。所以,一般建议使用分离执行体的方式执行子进程。
分离执行体
分离执行体就是将上面28,29,30,31行注释掉,然后将40,41行的注释打开。CefSettings中的browser_subprocess_path即是指定分离执行体的路径。CEFRender是新建的另一个工程,代码如下:
int WINAPI wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
//支持DPI,否则页面显示有问题
CefEnableHighDPISupport();
CefMainArgs main_args(hInstance);
CefRefPtr<BrowserApp> app(new BrowserApp);
return CefExecuteProcess(main_args, app, NULL);
}
同样在分离执行体进行了打印,执行主应用程序,结果如下:
在任务管理器中可见,主进程browser进程属于CEFDemo执行体,gpu和render进程属于CEFRender执行体。
由此可见,在分离执行体执行子进程,可删除不必要的代码,为子进程定制化程序,提高进程的启动速度,减少开销。