chromium Mojo介绍

目录

  • 什么是IPC?
  • 常见的IPC及简介
  • Chromium的IPC Mojo简介
  • Chromium 中IPC的调用

什么是IPC?

       IPC,全称Inter-Process Communication,字面意思就是进程间通信或者跨进程通信。更具体的一点,就是指两个进程交换数据的过程。 这里需要先解释下进程,进程指在系统中正在运行的一个应用程序,是系统资源分配的基本单位,在内存中有其完备的数据空间和代码空间,拥有完整的虚拟空间地址,一个进程所拥有的数据和变量只属于它自己。我们的每个进程并不是都完全独立运行,有时候会需要两个进程进行数据、消息交互,这就是IPC。

 

常见的IPC及简介

        Linux 种IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket等,其中 Socket和Streams支持不同主机上的两个进程IPC。安卓常见的IPC Bundle、Messenger、AIDL、ContentProvider、BroadcastReceiver、socket、文件共享。

 管道:

        它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。 只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中

消息队列:       

        消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

信号量:

        信号量(semaphore)它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。用于进程间同步,若要在进程间传递数据需要结合共享内存。基于操作系统的 PV操作,程序对信号量的操作都是原子操作。每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。支持信号量组。

共享内存:

        指两个或多个进程共享一个给定的存储区。是最快的一种IPC,因为进程是直接对内存进行存取,多个进程可以同时操作。

Socket:

        socket API原本是为网络通讯设计的,但是后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率。UNIX Domain Socket  不需要经过网络协议栈,不需要打包拆包,不需要计算校验和,不需要维护序号和应答等。UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已经存在,则bind()错误返回。

Bundle:

         Android 四大组件中 Activity、Service、Receiver 都支持在 Intent 中附加传递 Bundle 数据。Bundle 是以键值对的形式存储数据,支持基本数据类型、数组、实现 Serializable 或 Parcelable 接口的对象以及一些 Android 支持的特殊对象。使用 Bundle 的 put 与 get 方法族将数据保存至 bundle 对象,调用 Intent 的 putExtras 方法即可。

Messenger:

         Messenger是一种轻量级IPC方案,其底层实现原理就是AIDL,它对AIDL做了一次封装,所以使用方法会比AIDL简单,

由于它的效率比较低,一次只能处理一次请求,所以不存在线程同步的问题。

AIDL:

         通过AIDL,可以在一个进程中获取另一个进程的数据和调用其暴露出来的方法,从而满足进程间通信的需求.

ContentProvider:

        ContentProvider的底层是采用 Android中的Binder机制,进行添加、删除、获取 & 修改(更新)数据

BroadcastReceiver:     

       BroadcastReceiver(广播接收器)是Android四大组件之一,顾名思义,通过广播的方式进行消息传递,其本质是一个全局的监听器,可以监听到各种广播,可以用来实现不同组件之间的通信。广播最大的特点就是发送方并不关心接收方是否接到数据,也不关心接收方是如何处理数据的,通过这样的形式来达到接、收双方的完全解耦合。

 Chromium的IPC Mojo简介

       chromium apk 运行,会创建多个线程,多个线程之间的消息交互,chromium用了Mojo替代了早起的IPC。我在上一篇文章中转译了chromium官网了关于mojo的介绍,不了解的可以看看:

      https://blog.csdn.net/mengxin00100/article/details/106325249

Chromium 中IPC的调用

      这里我以Render 为例进行解析说明

      1、发送消息种类(Message Type)

      主要分为2类:“routed” 和 “control”。

enum class MessageKind {
  CONTROL,
  ROUTED,
};

    代码路径ipc/ipc_message_templates.h

#define IPC_MESSAGE_CONTROL(msg_class, ...) \
  IPC_MESSAGE_DECL(msg_class, CONTROL, IPC_TUPLE(__VA_ARGS__), void)
#define IPC_MESSAGE_ROUTED(msg_class, ...) \
  IPC_MESSAGE_DECL(msg_class, ROUTED, IPC_TUPLE(__VA_ARGS__), void)

// Synchronous messages have both in and out parameters, so the lists need to
// be parenthesized to disambiguate:
//      IPC_SYNC_MESSAGE_CONTROL(BarMsg, (int, int), (bool))
//
// Implementation detail: The parentheses supplied by the caller for
// disambiguation are also used to trigger the IPC_TUPLE invocations below,
// so "IPC_TUPLE in" and "IPC_TUPLE out" are intentional.
#define IPC_SYNC_MESSAGE_CONTROL(msg_class, in, out) \
  IPC_MESSAGE_DECL(msg_class, CONTROL, IPC_TUPLE in, IPC_TUPLE out)
#define IPC_SYNC_MESSAGE_ROUTED(msg_class, in, out) \
  IPC_MESSAGE_DECL(msg_class, ROUTED, IPC_TUPLE in, IPC_TUPLE out)

   代码路径ipc/ipc_message_macros.h   

     1.1、routed消息,主要是用来给某个RenderViewHost对象发送消息的。不过,任何类都可以通过GetNextRoutingID 和 AddRoute 注册,就能接收routed消息。

     1.2、control消息由创建pipe的类处理,当然这些类也可以接收routed消息。

     1.3、消息的声明

IPC_MESSAGE_ROUTED0(FrameHostMsg_Detach)

   这个宏用来声明routed消息,这里声明了一个从render进程发送到browser进程的消息

IPC_MESSAGE_CONTROL0(FrameMsg_MyMessage)

    这个宏用来声明control消息,这里声明了一个从browser进程发送到render进程的消息,没有参数。

   消息名称表明消息的接受者,FrameHostMsg,带Host后缀的类,表示在browser进程接收处理的消息,FrameMsg,则表示在render进程处理的消息     

    2、消息通道是怎么建立的 (Message Channel)

    先看看channel是如何创建的,调用channel初始:

  mojo::PendingRemote<IPC::mojom::ChannelBootstrap> legacy_ipc_bootstrap;
  mojo::ScopedMessagePipeHandle legacy_ipc_channel_handle =
      legacy_ipc_bootstrap.InitWithNewPipeAndPassReceiver().PassPipe();
  channel_->Init(IPC::ChannelMojo::CreateClientFactory(
                     std::move(legacy_ipc_channel_handle),
                     ChildProcess::current()->io_task_runner(),
                     ipc_task_runner_ ? ipc_task_runner_
                                      : base::ThreadTaskRunnerHandle::Get()),
                 /*create_pipe_now=*/true);

代码路径:content/child/child_thread_impl.cc 

判断channel是否创建,如果没有,则创建channel    

void ChannelProxy::Init(std::unique_ptr<ChannelFactory> factory,
                        bool create_pipe_now) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!did_init_);

  DCHECK(!context_->quota_checker_);
  context_->quota_checker_ = factory->GetQuotaChecker();

  if (create_pipe_now) {
    // Create the channel immediately.  This effectively sets up the
    // low-level pipe so that the client can connect.  Without creating
    // the pipe immediately, it is possible for a listener to attempt
    // to connect and get an error since the pipe doesn't exist yet.
    context_->CreateChannel(std::move(factory));
  } else {
    context_->ipc_task_runner()->PostTask(
        FROM_HERE,
        base::BindOnce(&Context::CreateChannel, context_, std::move(factory)));
  }

  // complete initialization on the background thread
  context_->ipc_task_runner()->PostTask(
      FROM_HERE, base::BindOnce(&Context::OnChannelOpened, context_));

  did_init_ = true;
  OnChannelInit();
}

代码路径:ipc/ipc_channel_proxy.cc

进而调用MojoBootstrap::Create,

std::unique_ptr<MojoBootstrap> MojoBootstrap::Create(
    mojo::ScopedMessagePipeHandle handle,
    Channel::Mode mode,
    const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
    const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker) {

  return std::make_unique<MojoBootstrapImpl>(
      std::move(handle), new ChannelAssociatedGroupController(
                             mode == Channel::MODE_SERVER, ipc_task_runner,
                             proxy_task_runner, quota_checker));
}

代码路径:ipc/ipc_mojo_bootstrap.cc

 在MojoBootstrapImpl里完成sender和listener的绑定:

class MojoBootstrapImpl : public MojoBootstrap {
 public:
  MojoBootstrapImpl(
      mojo::ScopedMessagePipeHandle handle,
      const scoped_refptr<ChannelAssociatedGroupController> controller)
      : controller_(controller),
        associated_group_(controller),
        handle_(std::move(handle)) {}

  ~MojoBootstrapImpl() override {
    controller_->ShutDown();
  }

 private:
  void Connect(
      mojo::AssociatedRemote<mojom::Channel>* sender,
      mojo::PendingAssociatedReceiver<mojom::Channel>* receiver) override {
    controller_->Bind(std::move(handle_));
    controller_->CreateChannelEndpoints(sender, receiver);    
    //base::debug::StackTrace().Print();
    CHROMIUM_LOGD("<ipc_mojo_bootstrap.cc>[%s][%d]\n",__FUNCTION__,__LINE__);
  }
......
};

进程里都是使用IPC::ChannelProxy这个类来代理完成Channel的各种工作。

mojo会负责将channel两端连通,之后的消息发送就可使用IPC::ChannelProxy来完成

3、发送者和接收者(Sender,Listener)

  1、发送者,chromium里定义了IPC::Sender的接口:

namespace IPC {

class Message;

class COMPONENT_EXPORT(IPC) Sender {
 public:
  // Sends the given IPC message.  The implementor takes ownership of the
  // given Message regardless of whether or not this method succeeds.  This
  // is done to make this method easier to use.  Returns true on success and
  // false otherwise.
  virtual bool Send(Message* msg) = 0;

 protected:
  virtual ~Sender() {}
};

}  // namespace IPC

代码路径:ipc/ipc_sender.h

发送者在其接口上进行实现

2、接收者,chromium里定义了IPC::Listener的接口:

namespace IPC {

class Message;

// Implemented by consumers of a Channel to receive messages.
class COMPONENT_EXPORT(IPC) Listener {
 public:
  // Called when a message is received.  Returns true iff the message was
  // handled.
  virtual bool OnMessageReceived(const Message& message) = 0;

  // Called when the channel is connected and we have received the internal
  // Hello message from the peer.
  virtual void OnChannelConnected(int32_t peer_pid) {}

  // Called when an error is detected that causes the channel to close.
  // This method is not called when a channel is closed normally.
  virtual void OnChannelError() {}

  // Called when a message's deserialization failed.
  virtual void OnBadMessageReceived(const Message& message) {}

  // Called when an associated interface request is received on a Channel and
  // the Channel has no registered handler for it.
  virtual void OnAssociatedInterfaceRequest(
      const std::string& interface_name,
      mojo::ScopedInterfaceEndpointHandle handle) {}

#if defined(OS_POSIX) || defined(OS_FUCHSIA)
  // Called on the server side when a channel that listens for connections
  // denies an attempt to connect.
  virtual void OnChannelDenied() {}

  // Called on the server side when a channel that listens for connections
  // has an error that causes the listening channel to close.
  virtual void OnChannelListenError() {}
#endif  // OS_POSIX || OS_FUCHSIA

 protected:
  virtual ~Listener() {}
};

}  // namespace IPC

代码路径:ipc/ipc_listener.h

发送者在其接口上进行实现。

可以结合browser线程跟render线程一起看,会有助于理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值