`
hyshucom
  • 浏览: 814354 次
文章分类
社区版块
存档分类
最新评论

剑走偏锋的 Native Clien

 
阅读更多

原文:http://www.guao.hk/posts/cutting-edge-native-client.html

不知不觉,Google已经正式推出其Native Client (NaCl)过去约7个月之久。而目前国内似乎还没有多少关于NaCl的资料,所以在这里面向Web开发者做一下简单的介绍,希望能够起到一个抛砖引玉的效果。

本文的所有代码均来自于https://developers.google.com/native-client/devguide/tutorial,如果您对其中的任何技术细节存在疑问,请以原文为准。

何谓NaCl

NaCl是一项能让C/C++代码运行在浏览器当中的技术。这是一个最通俗的说法,但不够精确。严格来说,NaCl技术在理论上能够实现任何编译型语言都在其之上运行。

但是目前由于技术上的原因,NaCl还做不到任何语言任何平台的编译型语言支持。

因为NaCl所憧憬的实际是LLVM技术,LLVM技术的要点即在于能将编译型语言转化为一个统一的中间语言,NaCl通过对这个中间语言的执行,即可达成任何编译型语言的运行。换句话说,NaCl实际上希望搭建一个虚拟机。

不过LLVM现在还不够成熟,NaCl不得不先使用GNU的编译套件,使用LLVM技术的版本被称为了PNaCl,目前还没有正式推出。同时也因为这个原因,ARM架构没能正式支持。

为什么NaCl

在开发层面上,NaCl希望解决一个问题:JavaScript的低效率。当然,从经营策略上来说,Google可能还希望籍此将桌面领域的成熟软件快速移植到其Chrome OS当中,不过这不是我们讨论的重点。

JavaScript毕竟是一门解释型语言,只有当浏览器执行到代码的时刻才能够看到代码,因此在执行优化上力度非常小。

但是Web应用发展至今,效率已经必须拿到桌面上考虑,如果你还想在浏览器里面看到更多优质的游戏的话。

有关限制

  • 缺乏能够切合的IDE
  • 不支持硬件异常
  • 不支持创建子进程
  • 不支持原生TCP/UDP操作(但已提供了websocket支持)
  • 不支持同步I/O
  • 不支持内存剩余查询
  • 内联汇编代码必须通过NaCl验证
  • NaCl的Pepper API必须通过主线程使用

如何使用

上面那些对于行动主义来说其实都是P,真正需要聚焦的还是如何使用。

NaCl的典型项目由三个部分组成:

  • 网页(*.html)。这里所指的网页是一个泛指,它包括JS代码、CSS样式表已经HTML代码。
  • NaCl模块(*.c;*.cc)。这是C/C++代码的文件。
  • 清单(*.nmf)。这份清单类似于Chrome Extension的清单,主要用于指明在不同架构的机器上调用什么模块。

在真正开始之前,我们还需要安装一个NaCl的SDK。这个SDK当中主要包含了NaCl的模块编译工具链。大家可以从这里下载:https://developers.google.com/native-client/sdk/download

安装之前,确保一下机器上有一个可用的Python 2.7,并加入到环境变量当中。

而SDK的安装则相当简单,只需要使用naclsdk update命令即可。

下面,创建一个名为hello_tutorial的目录,我们来搭建一个简单的demo。

网页
<!DOCTYPE html>
<html>
<head>
  <title>hello_tutorial</title>

  <script type="text/javascript">
    hello_tutorialModule = null;  // 模块的全局对象
    statusText = 'NO-STATUS';

    // 更新状态
    function moduleDidLoad() {
      hello_tutorialModule = document.getElementById('hello_tutorial');
      updateStatus('SUCCESS');
	  // 向模块发送消息
	  hello_tutorialModule.postMessage('hello');
    }

    // 消息句柄函数。句柄在NaCl模块发送相应消息时自动唤起。
	// 在C中是PPB_Messaging.PostMessage(),C++中则是pp::Instance.PostMessage()
	// 在这个demo当中,我们收到消息之后弹窗示意

    function handleMessage(message_event) {
      alert(message_event.data);
    }

    // 页面载入时很可能NaCl模块还没有载入,因此我们将状态写为正在读取;
	// 而如果模块已经载入,则什么都不做。
    function pageDidLoad() {
      if (hello_tutorialModule == null) {
        updateStatus('LOADING...');
      } else {
		// 事实上,NaCl模块的载入成功事件是不可能在页面载入成功事件之前就发生的,
		// 因此我们这里简单的认为页面载入之后所更新显示的消息仍旧是当前消息,而不是'SUCCESS'。
        updateStatus();
      }
    }

	// 设置状态。如果存在id为'statusField'的元素,那么将其设置为参数携带的状态
    function updateStatus(opt_message) {
      if (opt_message)
        statusText = opt_message;
      var statusField = document.getElementById('status_field');
      if (statusField) {
        statusField.innerHTML = statusText;
      }
    }
  </script>
</head>
<body onload="pageDidLoad()">

<h1>Native Client Module hello_tutorial</h1>
<p>
  <!-- 读取.nexe文件。通过.nmf,浏览器将结合目前所处的CPU架构来读取不同的模块文件。
  于此同时,<embed>元素之外的<div>还绑定着两个事件监听('load'以及'message'),
  之所以绑定在div上,而不是embed之上,是为了确保模块在载入之前就可以将监听的绑定彻底完成,
  同时也确保了开发者可以在模块内部的初始化阶段调用API发送消息。
  -->
  <div id="listener">
    <script type="text/javascript">
      var listener = document.getElementById('listener');
      listener.addEventListener('load', moduleDidLoad, true);
      listener.addEventListener('message', handleMessage, true);
    </script>

    <embed name="nacl_module"
       id="hello_tutorial"
       width=0 height=0
       src="hello_tutorial.nmf"
       type="application/x-nacl" />
  </div>
</p>

<h2>Status</h2>
<div id="status_field">NO-STATUS</div>
</body>
</html>
NaCl模块
/// @file hello_tutorial.cc
/// 载入NaCl模块时,浏览器首先将搜索CreateModule()方法,CreateModule()会返回一个对象实例,
/// 之后浏览器会调用该实例的CreateInstance()方法,这时浏览器每遇到一次相应的<embed>就会调用一次。
///
/// 浏览器通过Javascript的postMessage()函数与NaCl通信。
/// 当调用postMessage()时,浏览器会转而调用pp::Instance的HandleMessage()方法。
/// 而如果模块需要与外界主动通信,则是使用pp::Instance的postMessage()方法。
/// 注意,这两个postMessage()都是异步的,因此两者在调用之后迅速返回。

#include <cstdio>
#include <string>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"

namespace {
// 这个字符串用来判断消息是否是我们期望的内容
const char* const kHelloString = "hello";
// 这个字符串用来向浏览器返回内容
const char* const kReplyString = "hello from NaCl";
} // namespace

/// 每一个NaCl模块都将有一个相应的pp::Instance子类实例对应,
/// 为了与浏览器进行通信,你必须重载 HandleMessage()方法以及PostMessage()方法
class hello_tutorialInstance : public pp::Instance {
 public:

  explicit hello_tutorialInstance(PP_Instance instance) : pp::Instance(instance)
  {}
  virtual ~hello_tutorialInstance() {}

  /// HandleMessage() 负责接收浏览器中postMessage()发送的消息内容
  /// 其中的参数几乎可以表示任何东西,但通常都是JSON字符串,比如这样:
  ///   var json_message = JSON.stringify({ "myMethod" : "3.14159" });
  ///   nacl_module.postMessage(json_message);
  virtual void HandleMessage(const pp::Var& var_message) {
    // 这里是处理消息的代码了
	if (!var_message.is_string())
		return;
	std::string message = var_message.AsString();
	pp::Var var_reply;
	if (message == kHelloString) {
		var_reply = pp::Var(kReplyString);
		PostMessage(var_reply);
	}
  }
};

class hello_tutorialModule : public pp::Module {
 public:
  hello_tutorialModule() : pp::Module() {}
  virtual ~hello_tutorialModule() {}

  virtual pp::Instance* CreateInstance(PP_Instance instance) {
    return new hello_tutorialInstance(instance);
  }
};

namespace pp {
Module* CreateModule() {
  return new hello_tutorialModule();
}
}  // namespace pp
Makefile

Makefile是C\C++编译指令的存放文件,这份文件将指引编译器、链接器如何工作。因为我们现在所处情况特殊,所以Makefile需要自己编写。

# Copyright (c) 2012 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

#
# GNU Make based build file.  For details on GNU Make see:
# http://www.gnu.org/software/make/manual/make.html
#

#
# Project information
#
# 这里的变量将指出每个项目的编译信息,如项目名、编译标志等等。
# 通常每个项目都将有所不同。这里只是作为一个demo,因此只有一些最简单的信息。
# 注意,编辑这里的内容需要你对编译器有足够的了解
#
PROJECT:=hello_tutorial
LDFLAGS:=-lppapi_cpp -lppapi
CXX_SOURCES:=$(PROJECT).cc

#
# 设置pepper目录
#
THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST)))
NACL_SDK_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..)

# Project Build flags
WARNINGS:=-Wno-long-long -Wall -Wswitch-enum -pedantic -Werror
CXXFLAGS:=-pthread -std=gnu++98 $(WARNINGS)

#
# 设置工具路径
#
#
OSNAME:=$(shell python $(NACL_SDK_ROOT)/tools/getos.py)
TC_PATH:=$(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_x86_newlib)
CXX:=$(TC_PATH)/bin/i686-nacl-g++

#
# Cygwin专用设置
#
CYGWIN ?= nodosfilewarning
export CYGWIN

# Declare the ALL target first, to make the 'all' target the default build
all: $(PROJECT)_x86_32.nexe $(PROJECT)_x86_64.nexe

# 32位文件生成规则
x86_32_OBJS:=$(patsubst %.cc,%_32.o,$(CXX_SOURCES))
$(x86_32_OBJS) : %_32.o : %.cc $(THIS_MAKE)
        $(CXX) -o $@ -c $< -m32 -O0 -g $(CXXFLAGS)

$(PROJECT)_x86_32.nexe : $(x86_32_OBJS)
        $(CXX) -o $@ $^ -m32 -O0 -g $(CXXFLAGS) $(LDFLAGS)

# 64位文件生成规则
x86_64_OBJS:=$(patsubst %.cc,%_64.o,$(CXX_SOURCES))
$(x86_64_OBJS) : %_64.o : %.cc $(THIS_MAKE)
        $(CXX) -o $@ -c $< -m64 -O0 -g $(CXXFLAGS)

$(PROJECT)_x86_64.nexe : $(x86_64_OBJS)
        $(CXX) -o $@ $^ -m64 -O0 -g $(CXXFLAGS) $(LDFLAGS)

# Define a phony rule so it always runs, to build nexe and start up server.
.PHONY: RUN
RUN: all
  python ../httpd.py
清单文件

清单中指出了不同架构所使用的模块。

{
  "program": {
    "x86-64": {"url": "hello_tutorial_x86_64.nexe"},
    "x86-32": {"url": "hello_tutorial_x86_32.nexe"}
  }
}

所有这些文件齐全之后,只需要make即可完成自动编译。

如何运行

在编译之后,双击html页面打开其实并不会载入模块运行,这是由于浏览器的访问域规则不允许直接在用户的本地读取文件。因此,我们需要让本机成为一个服务器,以远程服务器的身份来读取模块最终传送给浏览器。

这很容易,SDK当中已经包含了提供http服务的python脚本:

cd pepper_18/examples #注意,pepper的版本号应该依据你现有的情况来定
python httpd.py

这样,http://localhost:5103就成为了我们的服务器地址。注意,http服务的根目录的位置位于examples目录。

现在,我们在浏览器的chrome://flagschrome://plugins页面中打开NaCl的几个相关选项,就可以成功运行了。


分享到:
评论

相关推荐

    星际防御战:剑走偏锋的传统塔防.docx

    星际防御战:剑走偏锋的传统塔防.docx

    剑走偏锋--AMD移动CPU本本初体验.pdf

    剑走偏锋--AMD移动CPU本本初体验.pdf

    订菜网朱晓义:剑走偏锋 半成品搭配先行者.docx

    订菜网朱晓义:剑走偏锋 半成品搭配先行者.docx

    竞价创意技巧篇 剑走偏锋的奇招:吸睛大法.pdf

    竞价创意技巧篇 剑走偏锋的奇招:吸睛大法.pdf

    [KCon 2016]0828_1_朱利军_剑走偏锋之 Hacking 无处不在.pdf

    [KCon 2016]0828_1_朱利军_剑走偏锋之 Hacking 无处不在 大数据

    寝室建网走偏锋——学生用无线路由器选购小贴士.pdf

    寝室建网走偏锋——学生用无线路由器选购小贴士.pdf

    程序员进阶修炼说明35岁前要培养的66种明智思维

     掌握剑走偏锋的思维方式和做事方法,敢于颠覆与重建思维秩序,反弹琵琶追求成功,是每一个35岁前意图成就一番事业的人在新时代应该上的必修课。  2  第二部分:行闯职场江湖——厚黑学问与潜在规则  职场江湖...

    Jquery1.2.6源码分析

    jQuery是一个非常优秀的JS库,与Prototype,YUI,Mootools等众多的Js类库相比,它剑走偏锋,从web开发的实用角度出发,抛除了其它Lib中一些中看但不实用的东西,为开发者提供了优美短小而精悍的类库。其使用简单,文档...

    学习.net 《你必须知道的.NET》

    2 1.1 对象的旅行 3 本节将介绍以下内容: 4 — 面向对象的基本概念 5 — .NET基本概念评述 6 — 通用类型系统 7 1.1.1 引言 8 提起面向对象,每个程序设计者都有自己的理解,有的深入肌理,有的剑走偏锋。...

    第三方系统对接CBS APP流程

    跨界创新的CBS系统,突破了传统商业银行服务边界,在不断演进更迭中,逐渐形成了招行独具特色的跨银行现金管理服务体系,成为招行在公司金融领域“剑走偏锋”差异化发展的拳头产品。 对接开发文档。

    你必须知道的.net

    非常好的.net开发书籍,提起面向对象,每个程序设计者都有自己的理解,有的深入肌理,有的剑走偏锋。但是无论所长,几个基本的概念总会得到大家的重视,它们是:类、对象、继承、封装和多态。很对,差不多就是这些...

    【完整源码】基于Qt/MySQL的学生管理系统

    基于MySQL的B/S架构信息系统,剑走偏锋利用Qt实现了界面的设计(作为数据库入门练手项目再适合不过了)欢迎参考。

    35岁前要培养的66种明智思维

    掌握剑走偏锋的思维方式和做事方法,敢于颠覆与重建思维秩序,反弹琵琶追求成功,是每一个35岁前意图成就一番事业的人在新时代应该上的必修课。

    你必须知道的.NET

    每个程序设计者都有自己的理解,有的深入肌理,有的剑走偏锋。但是无论所长,几个基本的概念总会得到大家的重视,它们是:类、对象、继承、封装和多态。

    全栈工程师修炼指南 下载

    11-剑走偏锋:面向切面编程.mp3 12-唯有套路得人心:谈谈JavaEE的那些模式.mp3 13-特别放送:选择比努力更重要.mp3 14-别有洞天:从后端到前端.mp3 15-重剑无锋,大巧不工: JavaScript面向对象.mp3 16-百花齐放,...

    [pdf]Jquery1.2.6源码分析

    库相比,它剑走偏锋,从web开发的实用角度出发,抛除了其它 Lib中一些中看 但不实用的东西,为开发者提供了优美短小而精悍的类库。其使用简单,文档丰 富,而且性能高效,能极大地提高 web 系统的开发效率。因此可以...

    KCon 2016PPT汇总(13份).zip

    6_何潇潇_VMProtect 的一次奇妙之旅.pdf', '%5bKCon 2016%5d0827_8_行之_Binder Fuzz based on drozer.pdf', '%5bKCon 2016%5d0828_1_朱利军_剑走偏锋之 Hacking 无处不在.pdf', '%5bKCon 2016%5d0828_2_Kevin2600_...

    DBA应该知道的一些SQL Server跟踪标记

     对于DBA来说,掌握Trace Flag是一个成为SQL Server高手的必要条件之一,在大多数情况下,Trace Flag只是一个剑走偏锋的奇招,不必要,但在很多情况下,会使用这些标记可以让你更好的控制SQL Server的行为。...

Global site tag (gtag.js) - Google Analytics