chromium进程间通信-ChannelImpl

我们先来了解chromium进程间通信的方式--命名管道。管道(Pipe)可能理解成是用于进程间通信的一段共享内存,创建管道的进程称为管道服务端server,连接到一个管道的进程为管道客户端client。命名管道有一个唯一的名称以区分于存在于系统中的其他命名管道。管道的命名格式如下:
\\Server\Pipe\[Path]Name

其中,第一部分\\Server指定了服务器的名字,命名管道服务即在此服务器创建,其字串部分可表示为一个小数点(表示本机)、星号(当前网络字段)、域名或是一个真正的服务;第二部分\Pipe在使用时不能变;第三部分\[Path]Name则使应用程序可以唯一定义及标识一个命名管道的名字,可以设置多级目录。chromium使用的命名是

\\.\pipe\chrome.$channel_id

也就是在”\\.\pipe\chrome.“后接一个channel_id为管道命名。

命名管道在服务端需要调用CreateNamedPipe创建管道,然后调用ConnectNamedPipe接收来自客户端的连接。在客户端,使用CreateFile打开管道。管道服务端在调用CreateNamedPipe创建命名管道需要为指定管道名称。对于管道客户端,则是在调用CreateFileW函数连接命名管道时对管道名称进行指定。在管道双方都可以进行读写的操作,如WriteFile、ReadFile,或者调用CloseHandle断开连接。命名管道的使用过程很像socket,只是调用的方法不一样。

chromium中管道的创建和收发数据最终都由一个类Channel::ChannelImpl::ChannelImpl完成,为了进行管道的收发,定义了两个state,分别是input_state_和output_state_,其中input_state_在用于接收客户端连接或者接收数据时使用,而output_state_则在发送数据时使用,在以下函数中都可以看到这两个state的使用。

BOOL ok = ReadFile(pipe_, buffer, buffer_len,
                     &bytes_read, &input_state_.context.overlapped);
BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
BOOL ok = WriteFile(pipe_,
                    m->data(),
                    static_cast<int>(m->size()),
                    &bytes_written,
                    &output_state_.context.overlapped);

先来看看state的定义,它有两个重新的成员,is_pending表示一个接收数据、发送数据事件是否在进行中。input_state_在服端端进行等待接收客户端连接,或者服务端客户端接收数据的过程中,is_pending都会设置成true;output_state_在进行数据发送的过程中,is_pending会设置成true。在io事件处理完成后,is_pending会在OnIOCompleted函数中进行重置。在Channel::ChannelImpl::Send的处理逻辑中,可以发现,如果output_state_.is_pending设为true的话,消息会被放到一个output_queue_中,不会直接发送,保证管道同一时间只发送一个消息。

struct State {
  explicit State(ChannelImpl* channel);
  ~State();
  base::MessageLoopForIO::IOContext context;
  bool is_pending;
};
Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
  memset(&context.overlapped, 0, sizeof(context.overlapped));
  context.handler = channel;
}
state另一个属性context,它的类型是IOContex,它的定义为

struct IOContext {
    OVERLAPPED overlapped;
    IOHandler* handler;
};
overlapped主要用于区别完成的io事件,在上文调用ReadFile、 ConnectNamedPipe、WriteFile的例子中都有使用。在io事件完成后,由GetQueuedCompletionStatus捕获。handler是指向Channel::ChannelImpl::ChannelImpl自已的指针,因为ChannelImpl继承了MessageLoopForIO::IOHandler接口,实现了OnIOCompleted函数。

void Channel::ChannelImpl::OnIOCompleted(
    base::MessageLoopForIO::IOContext* context,
    DWORD bytes_transfered,
    DWORD error)
注意到,overlapped的地址其实就是IOContext的地址,在MessagePumpForIO中,它们的地址就是直接这样转换的
item->context = reinterpret_cast<IOContext*>(overlapped);  

在OnIOCompleted中,通过context是等于input_state_.context还是output_state_.context判断完成的是输入事件还是输出事件,从而做出相应的处理。在对于接收处理,分为两种:

1、等待客户端连接完成,对于服务端waiting_connect_在初始是为true,首先调用ProcessConnection检查连接是否建立完成,正常的情况下ProcessConnection都会返回true,这时候OnIOCompleted返回。

2、异步接收数据完成,这时候调用AsyncReadComplete把接收完成的bytes_transfered个字节与之前接收的字节组合起来,看是否完成了一整条消息,如果完成了消息,则调用消息处理函数对消息进行处理。若管道没有异常,ok都会为真值,调用ProcessIncomingMessages函数,接着进行异步的数据读取。

void Channel::ChannelImpl::OnIOCompleted(
    base::MessageLoopForIO::IOContext* context,
    DWORD bytes_transfered,
    DWORD error) {
  bool ok = true;
  if (context == &input_state_.context) {
    if (waiting_connect_) {
      if (!ProcessConnection())
        return;
      if (!output_queue_.empty() && !output_state_.is_pending)
        ProcessOutgoingMessages(NULL, 0);
      if (input_state_.is_pending)
        return;
    }
    base::AutoReset<bool> auto_reset_processing_incoming(
        &processing_incoming_, true);
    if (input_state_.is_pending) {
      input_state_.is_pending = false;
      if (!bytes_transfered)
        ok = false;
      else if (pipe_ != INVALID_HANDLE_VALUE)
        ok = AsyncReadComplete(bytes_transfered);
    } else {
      DCHECK(!bytes_transfered);
    }
    if (ok)
      ok = ProcessIncomingMessages();
  } else {
    ok = ProcessOutgoingMessages(context, bytes_transfered);
  }
  if (!ok && INVALID_HANDLE_VALUE != pipe_) {
    Close();
    listener()->OnChannelError();
  }
}

在处理发送完成事件时,如果output_state_.is_pending为真,表示上一条消息在等待发送,而这时这条消息的发送已经完成,把消息从队列中删除。然后查看队列中是否还有其它需要发送的消息,接着调用WriteFile发送消息,注意在异步IO中WriteFile返回的一定是0,但是ERROR_IO_PENDING并不表明发送失败,只是等待异步操作完成。

bool Channel::ChannelImpl::ProcessOutgoingMessages(
    base::MessageLoopForIO::IOContext* context,
    DWORD bytes_written) {
  if (output_state_.is_pending) {
    output_state_.is_pending = false;
    if (!context || bytes_written == 0) {
      DWORD err = GetLastError();
      LOG(ERROR) << "pipe error: " << err;
      return false;
    }
    Message* m = output_queue_.front();
    output_queue_.pop();
    delete m;
  }
  if (output_queue_.empty())
    return true;
  if (INVALID_HANDLE_VALUE == pipe_)
    return false;
  Message* m = output_queue_.front();
  DCHECK(m->size() <= INT_MAX);
  BOOL ok = WriteFile(pipe_,
                      m->data(),
                      static_cast<int>(m->size()),
                      &bytes_written,
                      &output_state_.context.overlapped);
  if (!ok) {
    DWORD err = GetLastError();
    if (err == ERROR_IO_PENDING) {
      output_state_.is_pending = true;
      return true;
    }
    return false;
  }
  output_state_.is_pending = true;
  return true;
}
管道连接的逻辑调用Connect函数完成。对于服务端waiting_connect_的初始值为true,那么进入函数ProcessConnection后调用ConnectNamedPipe,这时分两种情况:

1、在服务端调用CreateNamedPipe之后调用ConnectNamedPipe函数之前,有客户端调用了CreateFile,这时候管道已经连接完成,GetLastError()返回ERROR_PIPE_CONNECTED,waiting_connect_设为false,input_state_.is_pending为false,这个状态表示连接已经建立,但是没还有开始等待接收数据,随后调用PostTask,进入OnIOCompleted函数,调用ProcessIncomingMessages等待接收数据。
2、如果没有客户端进行连接,这时GetLastError()返回ERROR_IO_PENDING,这时把input_state_.is_pending设为true,不调用OnIOCompleted,因为此时连接没有建立完成,不需要等待接收数据。

对于客户端,waiting_connect_初始设置为false,在调用CreateFile就能判断管道是否已经正常建立,只要pipe_不为INVALID_HANDLE_VALUE,那么管道已经建立了。只需要调用OnIOCompleted等待接收数据。

bool Channel::ChannelImpl::Connect() {
  if (!thread_check_.get())
    thread_check_.reset(new base::ThreadChecker());
  if (pipe_ == INVALID_HANDLE_VALUE)
    return false;
  base::MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
  if (waiting_connect_)
    ProcessConnection();
  if (!input_state_.is_pending) {
      base::MessageLoopForIO::current()->PostTask(
        FROM_HERE,
        base::Bind(&Channel::ChannelImpl::OnIOCompleted,
                   weak_factory_.GetWeakPtr(),
                   &input_state_.context,
                   0,
                   0));
  }
  if (!waiting_connect_)
    ProcessOutgoingMessages(NULL, 0);
  return true;
}
bool Channel::ChannelImpl::ProcessConnection() {
   if (input_state_.is_pending)
    input_state_.is_pending = false;
  if (INVALID_HANDLE_VALUE == pipe_)
    return false;
  BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
  DWORD err = GetLastError();
  if (ok) {
    NOTREACHED();
    return false;
  }
  switch (err) {
  case ERROR_IO_PENDING:
    input_state_.is_pending = true;
    break;
  case ERROR_PIPE_CONNECTED:
    waiting_connect_ = false;
    break;
  case ERROR_NO_DATA:
    return false;
  default:
    NOTREACHED();
    return false;
  }
  return true;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值