注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

写着玩

Bob

 
 
 

日志

 
 
 
 

chrome源码解析系列:chrome 进程间通讯  

2009-12-01 16:22:46|  分类: Chrome |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

进程之间是怎样通讯的呢?

在这里chrome是采用了命名管道(Named Pipes )机制。

 

    所谓Named Pipes,其实是一个WINDONWS内核对象,关于内核对象的介绍可以参考《WINDOWS核心编程》。注意Named Pipes 在NT以上的系统才被支持的。

Named Pipes分客户端和服务器端,服务器创建一个命名管道,客户端可以连接到这个管道上。WINDOWS对命名管道的支持非常完善,不当支持本地命名管道,还支持跨网络的命名管道,可以从一台机器上连接到另外一台机器。

    工作的基本流程是这样的:

    服务器和客户端确定命名管道的名称,这个名称是有讲究的,具体怎么给管道取名,同学们可以去网上查看资料。

    服务器:调用 CreateNamedPipe 创建一个命名管道,然后调用ConnectNamedPipe等待客户端的连接,采用WriteFile来传送数据,用ReadFile来读数据。

    客户端 : 调用CreateFile来连接 服务器,采用WriteFile来传送数据,用ReadFile来读数据。

其实同学们看见 CreteFile,WriteFile,ReadFile,不比担心它们会写磁盘,其实写的是系统的一块缓冲区,所以效率不比担心,最耗效率的地方其实就是系统调用和用户本地调用的切换说花费的时钟周期。

         chrome中使用Name Pipes采用了异步的调用的方式,这就是所谓的完成端口模型。这会增加一点理解的复杂度。关于完成端口,我们可以把系统看成服务器,我们的程序是客户端,当需做一个任务,比如服务器调用ConnectNamedPipe,如果先前创建Name Piples设置了FILE_FLAG_OVERLAPPED,那么调用该函数会立即返回,然后系统工作,当有连接到来的时候,系统会激活先前设置的事件,来通知程序。和WINDOWS窗口口号类似:“你别等我,我会通知你的“。

 

 

在Chrome中,BrowerProcess和RenderProcess, BrowerProcess和PluginProcess之间是怎么传递的呢?

又采用那些伎俩?

 

要寻求事物的本质吗,得先深入事物的无尽细节,了解透彻后,掉丢所有细节,剩下的就是本质。---bzero1982语录。

 

关于chrome进程间通讯,我们先反其道而行,先不了解细节,我们丢掉PluginProcess,丢掉BrowerProcess里面的各个线程,丢掉RenderProcess里面的各个线程,剩下在我们眼前的就是两个进程 BrowerProcess和RenderProcess,现在要研究的就是他们之间是怎么通讯的,当然了,后面会立马深入到BrowerProcess和RenderProcess内部,看看它们是一副什么筋骨?

 

Chrome中有一个叫Channel的对象,这个对象封装了NamePiple,所有的进程间通讯将通过这个对象来完成,也就是说,BrowerProcess和RenderProcess内部都分别拥有一个Channel的对象,用这个对象来实现彼此的互通。

那么接下的问题是:这个对象发送的字节流的格式是什么?

这就牵出了另外一个对象Message,Message是一个被精心定义的对象,它继承于Pickle,

Pickle是个对二进制进行读写操作的类,当然读写操作由它的派生类来定义。

 

Channel有个Send方法,参数就是Message类型。

 

现在来展示一段最原始的Message读写操作的代码:

/**
* @breif chrome进程间通讯测试程序
* @author bzero1982
*/

class ListenerImpl : public IPC::Channel::Listener
{
public:
    ListenerImpl() : other_(NULL) {
    }
    void Init(IPC::Message::Sender* s) {
        other_ = s;
    }
protected:
    IPC::Message::Sender* other_;
};

class ServerListener:public ListenerImpl
{
    virtual void OnMessageReceived(const IPC::Message& message)
    {
        std::cout<<"server:recive message:";
        IPC::MessageIterator iter(message); 
        const std::string data = iter.NextString();
        int index = iter.NextInt();
        std::cout<<"第"<<index<<"条消息:"<<data.c_str()<<std::endl;
    }

    virtual void OnChannelConnected(int32 peer_pid) 
    {
        std::cout<<"server:On connect"<<std::endl;
    }

    virtual void OnChannelError() 
    {
        std::cout<<"server:On connect error"<<std::endl;
    }
};


class ClientListener:public ListenerImpl 
{
    virtual void OnMessageReceived(const IPC::Message& message)
    {
        std::cout<<"client:recive message:";
        IPC::MessageIterator iter(message);
        int index = iter.NextInt();
        const std::string data = iter.NextString();
        std::cout<<"第"<<index<<"条消息:"<<data.c_str()<<std::endl;
    }
    virtual void OnChannelConnected(int32 peer_pid) 
    {
        std::cout<<"client:On connect"<<std::endl;
    }
    virtual void OnChannelError() 
    {
        std::cout<<"client:On connect error"<<std::endl;
    }
};

static void Send(IPC::Message::Sender* sender, const char* text) {
    static int message_index = 0;

    IPC::Message* message = new IPC::Message(0,2,
        IPC::Message::PRIORITY_NORMAL);
    message->WriteInt(message_index++);
    message->WriteString(std::string(text));
    sender->Send(message);
}

static bool RunClient() {
    // setup IPC channel
    ClientListener lister;
    IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_CLIENT,&lister);
    chan.Connect(); 
    lister.Init(&chan);
    Send(&chan, "hello from child");
    // run message loop
    MessageLoop::current()->Run();
    return true;
}

static bool RunServer() {
    // setup IPC channel
    ServerListener lister;
    IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER,&lister);
    chan.Connect();
    lister.Init(&chan);
    Send(&chan, "hello from Server");
    // run message loop
    MessageLoop::current()->Run();
    return true;
}
MessageLoop还没具体介绍,本来打算先介绍MessageLoop的,由于是研究消息系统研究到这里来了,所以先把这块给介绍了。还记得上一章说到的委托,在这里面Channel也把消息的接收,处理,连接出错状态委托给IPC::Channel::Listener的接口,由外部类来处理,这里是ServerListener和ClientListener.

这样理解:

RunServer好比是运行在BrowerProcess的一段代码,

RunClient是运行在RenderProcess里面的一段代码.

当服务器和客户端都连接后,相互发送了一段消息,

消息是先写int数据后写string数据,

 

 

message->WriteInt(message_index++);
message->WriteString(std::string(text));

然后看服务端和客户端的解包方式是:

 

virtual void OnMessageReceived(const IPC::Message& message)
    {
        std::cout<<"server:recive message:";
        IPC::MessageIterator iter(message); 
        const std::string data = iter.NextString();
        int index = iter.NextInt();
        std::cout<<"第"<<index<<"条消息:"<<data.c_str()<<std::endl;
    }


如果解包代码互换下:

 

const std::string data = iter.NextString();
int index = iter.NextInt();
调换了,那么读取数据的结果就会不正确。

这也就是我上面提到的,数据的读写规则由Message的派生类来定义。

 

这样两个进程直接发送数据,然后一堆问随之而来:

1:不是说BrowerProcess里面有个IO线程吗,IO线程用来接受RenderProces和网络下载的线程,那么当Render通过IPC把消息发送给IO线程了,IO线程怎么转发消息到BrowerProcess的主线程?

2:RenderProces中的IO线程和RenderProces中的主线程各自职责又是什么?他们之间又是怎么通讯的?

3:关于进程间异步和同步是怎么处理的?在什么情况下应用同步,什么情况下应用异步处理?

这些问题后面会慢慢解开的。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/bzero1982/archive/2008/10/27/3161879.aspx

  评论这张
 
阅读(660)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017