zoukankan      html  css  js  c++  java
  • chromium ②

    这篇研究两个问题:chromium对线程的封装和进程通信。主要参考chromium的官方技术文档:TreadingInter-process Communication (IPC)

    chrome速度快的优点,主要得益于线程模型的设计,这也是chrome最值得研究的技术点。

     
        一、线程模型
     
        chromium中的线程分为很多种类型,每个线程中有一个MessageLoop处理线程消息,对应实现:
        Thread - base/threading/thread.h
        MessageLoop - base/message_loop.h

        线程的类型包括:io_thread,file_thread,db_thread,safe_browsing_thread等,其中io_thread是一个事件分发线程,管理browser进程和子进程的通信。(上一篇多进程架构图中可以看到它。)
     
        chromium线程设计有一个非常重要的目标:保证UI线程的快速响应,也就是要确保UI线程中没有阻塞的I/O或耗时操作。保证这一点需要一个高效的多线程模型,面临的问题是:
        1、想尽一切办法减少多线程锁;
        2、多线程通信方式。
     
        chromium的实现手段:
        1、线程内消息循环的设计,为线程异步调用打下基础;
        2、Task封装,解决了加锁的问题,同时用command模式统一同步和异步调用。
        也就是说,Task是对一个任务处理的封装,而线程的消息循环都认识Task,这样整个多线程框架其实就是Task的传递和执行的过程,并且锁只在传递Task时发生。多么简单有效的模型!
     
        消息循环
        来看张图(引用自Chrome源码剖析
        chromium源码学习笔记(6) <wbr>-- <wbr>线程

        图示将线程的消息循环处理描述的非常清楚,基本思路和windows的消息循环非常相似。

    当然,不同类型的线程并不会处理所有的事件,但所有的消息循环都会处理Task。

        线程消息循环分为MessageLoop和MessagePump,其中MessageLoop专门处理Task,而MessagePump处理其他消息,
     
    发现Task时交给MessageLoop。不同线程的消息循环的MessageLoop和MessagePump如图:
     
    chromium源码学习笔记(6) <wbr>-- <wbr>线程


        Task    
        Task是对行为的封装,或者说是将处理函数封装为一个对象。Task对象有一个Run函数,供目标线程的消息循环调用,执行Task的处理。还是看张图:
    chromium源码学习笔记(6) <wbr>-- <wbr>线程
        如果A线程想让B线程处理一个事务,A只需创建一个Task,将事务处理代码封装好,找到B线程的MessageLoop对象,调用PostTask方法将Task插入消息队列。PostTask将立即返回,A线程继续处理自己的消息循环。B线程消息循环处理到Task时,调用其Run方法完成事务处理。
        chromium对Task的创建和传递做了封装,并定义了丰富的Task类型,满足多种场景需要。同时支持Task自身的日志和统计,便于调试。
        参考文章:
        
        二、进程通信(IPC)
     
        chromium的进程通信在Windows下采用命名管道(在Linux & Mac上采用socketpair),涉及到的进程主要是Browser process、Renderer process和Plugin process。
     
    (具体可以参考我的上一篇博文。)实际上最主要的通信发生在Browser和Renderer之间,基本上每一个Renderer process对应一个named pipe与Browser通信。
     
    管道通信采用异步模式,保证不会发生阻塞。
     
        根据上一篇博文提到的多进程模型图,可以知道Browser process中有一个I/O线程专门处理IPC,I/O线程和main thread之间通过ChannelProxy传递消息,I/O线程的主要目的是为了处理请求resource等可能阻塞的动作。Renderer process则用其main thread处理进程消息收发。
     
        命名管道根据定义好的规则生成,需要通信的两个进程都认识这个管道,之后就可以按照管道的技术特点通信了。
     
    chromium将这些逻辑用IPC::Channel封装,Channel处理三件事情:Send,Listen,处理Watcher,看张图:
    chromium源码学习笔记(6) <wbr>-- <wbr>线程
        
     
        发送进程调用Channel::Send将消息放到自己某线程的消息队列中,消息循环发现消息的信号量激活则序列化消息写到管道中。
     
    操作系统负责将管道内容进行传递并通知接收进程,接收进程有一个I/O类型的线程,其消息循环处理Watcher(参考前面的讲解),
     
    当发现Watcher的信号量被激活(收到消息),调用Watcher中对应的OnObjectSignaled方法,该方法通知本进程的Channel去管道中读取数据,并调用Listener解析处理。
     
        chromium用消息循环支撑了进程通信机制,并保证了异步处理防止阻塞。
     
        上面提到的ChannelProxy与Channel是什么关系呢?chromium为了保证线程安全,将Channel设计为由每个进程的I/O线程处理。
     
    如果进程中的非I/O线程想发送进程消息怎么办?一个简单思路是将消息发送封装成Task交给I/O线程去处理,chromium将这个处理封装成了ChannelProxy,方便使用。
     
    ChannelProxy甚至支持接收到消息后将其返回给原始发送消息的线程。基于以上,Browser的main thread想发送进程消息都必须通过ChannelProxy让I/O thread处理。
        有时需要同步的进程通信,这部分封装在ChannelProxy的子类SyncChannel中。
     
        chromium中的消息有两类:Routed消息和Control消息。简单来说,Routed消息有明确去向,会关联到一个视图上;
     
    Control消息不指定到视图,通常被创建管道的class处理。
        chromium为消息和Channel的使用封装了一套宏和模板,来简化进程通信的调用。具体可以参考:Inter-process Communication (IPC)
  • 相关阅读:
    JAVA运维总结篇
    python-30个骚操作
    seaweedfs文件存储服务器搭建
    Linux下nginx配置https协议访问
    微信公众平台应用号开发教程
    指导新人的一些心得
    <Android 基础(二十一)> Android 屏幕适配
    Java基础之引用(String,char[],Integer)总结于牛客网的专项练习题
    匿名内部类中关于new Runnable()的使用
    Java中的数据类型转换
  • 原文地址:https://www.cnblogs.com/dhsz/p/9252684.html
Copyright © 2011-2022 走看看