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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章