OSGi(Open Service Gateway Initiative)技术是Java动态化模块化系统的一系列规范。OSGi一方面指维护OSGi规范的OSGI官方联盟,另一方面指的是该组织维护的基于Java语言的服务(业务)规范。简单来说,OSGi可以认为是Java平台的模块层。
OSGi服务平台向Java提供服务,这些服务使Java成为软件集成和软件开发的首选环境。Java提供在多个平台支持产品的可移植性。OSGi技术提供允许应用程序使用精炼、可重用和可协作的组件构建的标准化原语,这些组件能够组装进一个应用和部署中。

背景

OSGi给出了一套Java模块化规范,这套规范给出了OSGi框架的定义,而具体的OSGi平台,如Felix和Equinox则分别是Apache和Eclipse开源社区给出的标准规范的实现。
OSGi服务平台提供在多种网络设备上无需重启的动态改变构造的功能。为了最小化耦合度和促使这些耦合度可管理,OSGi技术提供一种面向服务的架构,它能使这些组件动态地发现对方。OSGi联

OSGI

盟已经开发了例如像HTTP服务器、配置、日志、安全、用户管理、XML等很多公共功能标准组件接口。这些组件的兼容性插件实现可以从进行了不同优化和使用代价的不同计算机服务提供商得到。然而,服务接口能够基于专有权基础上开发。

因为OSGi技术为集成提供了预建立和预测试的组件子系统,所以OSGi技术使你从改善产品上市时间和降低开发成本上获益。因为这些组件能够动态发布到设备上,所以OSGi技术也能降低维护成本和拥有独一无二的新的配件市场机会。
 
 

安全协议

安全机制是建立在Java和Java2安全模型基础之上。Java语言的设计对很多结构进行了限

制。例如病毒中经常遇到的缓存溢出是不可能发生的。Java语言中的访问控制符限制了代码可见性。

OSGI平台通过使用私有类(在Java中不能用标准方式使用的机制)扩展了该模型。Java2安全模型提供了一个完整模块检查代码对于资源的可访问性。OSGI增加了完全动态的权限管理,简化了操作者和系统管理员的工作。
OSGI联盟已经定义了很多协议服务,这些服务将外部协议映射为OSGI服务。HTTP服务(HttpService)该HTTP服务是servlet运行器。bundles提供servlets,这些服务端小程序基于HTTP协议成为可用的。OSGi服务平台的动态更新功能使HTTP服务成为一个非常具有吸引力的Web服务器,它能伴随着新的servlet被更新,如果需要可以远程更新而无需重启。
UPnP服务(UPnPService)通用即插即用(UPnP)是一个正在形成中的消费电子标准。OSGi中的UPnP服务在一个UPnP网络上将设备映射到服务注册中。同样,它也可以将OSGi服务映射到UPnP网络。这是发布版本3中的推荐规范。
DMT管理(DMTAdmin)开放移动联盟(OMA)基于设备管理树为移动设备管理提供了一个完整规定。DMT管理服务定义该树如何被访问和/或者在OSGi服务平台中被扩充。
 
 

框架结构

OSGI规范的核心组件是OSGI框架。这个框架为应用程序(被叫做组件(bundle))提供了一个标准环境。整个框架可以划分为一些层次:
OSGI

L0:运行环境

L1:模块
L2:生命周期管理
L3:服务注册 [2] 
还有一个无处不在的安全系统渗透到所有层。
L0层执行环境是Java环境的规范。Java2配置和子规范,像J2SE,CDC,CLDC,MIDP等等,都是有效的执行环境。OSGi平台已经标准化了一个执行环境,它是基于基础轮廓和在一个执行环境上确定了最小需求的一个小一些的变种,该执行环境对OSGi组件是有用的。
L1模块层定义类的装载策略。OSGi框架是一个强大的具有严格定义的类装载模型。它基于Java之上,但是增加了模块化。在Java中,正常情况下有一个包含所有类和资源的类路径。OSGi模块层为一个模块增加了私有类同时有可控模块间链接。模块层同安全架构完全集成,可以选择部署到部署封闭系统,防御系统,或者由厂商决定的完全由用户管理的系统。
L2生命周期层增加了能够被动态安装、开启、关闭、更新和卸载的bundles。这些bundles依赖于于具有类装载功能的模块层,但是增加了在运行时管理这些模块的API。生命周期层引入了正常情况下不属于一个应用程序的动态性。扩展依赖机制用于确保环境的操作正确。生命周期操作在安全架构保护之下,使其不受到病毒的攻击。
L3层增加了服务注册。服务注册提供了一个面向bundles的考虑到动态性的协作模型。bundles能通过传统的类共享进行协作,但是类共享同动态安装和卸载代码不兼容。服务注册提供了一个在bundles间分享对象的完整模型。定义了大量的事件来处理服务的注册和删除。这些服务仅仅是能代表任何事物的Java对象。很多服务类似服务器对象,例如HTTP服务器,而另一些服务表示的是一个真实世界的对象,例如附近的一个蓝牙手机。这个服务模块提供了完整安全保障。该服务安全模块使用了一个很聪明的方式来保障bundles之间通信安全。
 

标准服务

在该框架之上,OSGi联盟定义了很多服务。这些服务通过一个Java接口指定。bundles能够实

OSGI

现这个接口,并在注册服务层注册该服务。服务的客户端在注册库中找到它,或者当它出现或者消失时做出响应。这个同SOA架构使用Web服务进行发布的方式相似。

两者主要不同是Web服务总是需要传输层,这个使它比采用直接方法调用的OSGi服务慢几千倍。同时,OSGi组件能够对这些服务的出现和消失做出响应。更多的信息可以从OSGi服务平台发行版本4手册或者PDF下载中找到。需要注意的是每一种服务都是抽象定义的,与不同计算机服务商的实现相独立。

框架服务

OSGi框架提供一个权限管理服务,一个包管理服务和一个开始级别服务。这些服务是一个可

OSGI

选部分,指示框架的操作。框架服务如下:

权限管理(PermissionAdmin)目前或者将来的bundles的权限通过这种服务进行维护。一旦设置了它们,权限服务立即激活。
包管理(PackageAdmin)bundles同类和资源分享包。bundles的更新可能需要系统重新计算这些依赖。这个包管理服务提供关于系统的实际包分享状态和能够刷新已经共享的包。也就是,取消依赖和重新计算依赖。
启动级别(StartLevel)启动级别是一个bundles集合,它们应该同时运行或者应该在其它已经启动以前被初始化。启动级别服务设置当前的启动级别,为每个bundle排一个启动级别和审核当前的设置。
URL处理者(URLHandler)Java环境为URL处理者支持一个提供者模型。然而,这是一个单件,不可能在一个象OSGi可能有很多提供者的协作环境上使用它。此服务规范使任何组件提供额外的URL处理者。

系统服务

系统服务提供水平功能,它在每个系统是必须的。日志服务,配置管理服务,设备访问

OSGI

服务,用户管理服务,IO连接器服务和参数服务都是系统服务的一个方面。

日志服务(LogService)日志信息,警告,调试或者错误信息通过日志服务来处理的。它接受日志实体并分派这些实体到订阅了这个信息的其他bundles。
配置管理服务(ConfigurationAdminService)该服务提供一个设置和获取配置信息的灵活、动态模型。
设备访问服务(DeviceAccessService)设备访问是OSGi为一个新的设备匹配一个驱动,并自动下载一个实现该驱动的bundles的机制。这个可用作即插即用方案。
用户管理服务(UserAdminService)该服务使用一个用于授权和验证目的的用户信息数据库。
IO连接器服务(IOConnectorService)该IO连接器服务实现了CDC/CLDCjavax包,并作为一个服务。该服务允许bundles提供新的可交换协议模式。
参数服务(PreferencesService)该服务提供了参数层级数据库的可访问性,同Windows注册表或者Java参数类相似。
组件运行时服务(ComponentRuntime)服务的动态特性--它们能够在任何时间来去自由--使编写软件变得更难。组建运行时规范通过提供一个基于依赖声明的XML文件来简化处理这些动态方面。
部署管理服务(DeploymentAdmin)OSGi的主要部署格式是bundle,它是一个JAR/ZIP文件。部署管理提供第二种可选格式:部署包。部署包能够将bundles和相应资源联接成可被安装和卸载的单个交付。完整的资源处理器模型允许用户代码扩充资源类型。
事件管理服务(EventAdmin)很多OSGi事件有特定的类型化的接口,使其很难接收和过滤事件。事件服务提供一个泛化的基于主题的事件机制。这个规范包括为所有已存框架和服务事件的映射。
应用程序管理服务(ApplicationAdmin)OSGibundle模型不同于依赖于启动和关闭形式的典型的桌面或者移动电话应用程序模型。该应用程序管理服务提供了传统应用程序模型和它所要求的管理设施。

HelloWorld

0.前言
这篇文档介绍如何使用OSGi框架的一个实现——Equinox来教你如何配置一个简单的OSGi开发环境,并且在这个环境上开发一个HelloWorld程序,这其中会涵盖前面的入门篇讲到的三个层次的内容,让你在实践的同时巩固之前了解的内容。话不多说,我们开始吧! [3] 
开发环境建立
1.1 Equinox是什么
从代码角度来看,Equinox其实就是OSGi核心标准的完整实现,并且还在这个基础上增加了一些额外的功能(比如为框架增加了命令行和程序执行的入口)。我们在之前入门篇讲解的东西其实都是OSGi核心标准的一小部分。其实它的核心就是一个jar包,这个jar包既能执行(作为标准Java包的特性),也是一个bundle(Manifest里面含有OSGi bundle特有的元数据)。
现在你需要知道的就是,我们能够利用Equinox项目的代码来运行一个实实在在的OSGi框架,框架启动后,你就可以将你开发好bundle放到里面运行。
1.2下载Equinox
Equinox在Eclipse官方网站上有下载,里面列出了各个版本供我们选择:
在这里,我们使用3.7版本的Equinox,下载好以后放在一个单独的文件夹下(这里我的路径是E:\OSGi framework\equinox)
1.3 从命令行启动框架
如果启动这个框架的话,有了上面的jar包就足够了,我们进入命令行输入如下命令:java –jar org.eclipse.osgi_3.7.0.v20110613.jar -console ,然后就会进入Equinox的控制台:
如果出现osgi>的提示符,就说明启动成功了。
Equinox的控制台的部分基本命令如下(区分大小写):
install [URL]
将URL表示的bundle安装到框架中
uninstall [bundleID]
将id=bundleID的bundle卸载
start [bundleID]
启动一个bundle
stop [bundleID]
停止一个bundle
refresh [bundleID]
刷新bundle
update [bundleID]
更新bundle 的内容
ss
简单显示所有bundle的状态
status
展示安装的bundle和注册的服务
headers [bundleID]
展示bundle 的manifest中的元数据
1.4 Eclipse建立环境
在上一节中大家看到启动和控制框架的方法,是相当简单的一个过程。不过单单只是运行环境还不够,我们还需要开发环境。
1.4.1 设置
首先我们不需要安装必须的插件,只要你有较新版本的Eclipse就行了。然后进入Eclipse的window->preferences界面,选中Plug-in Development下的Target Platform
现在右边只有一个Runing Platform的,我们任务是点击“Add…”按钮来增加一个我们自己的的platform,进入如下界面:
选择默认的第一个就好,点击next。
再点击这里的“Add…”:
选择“Directory”:
选择你的Equinox的jar包所在的路径,然后点击finish,回到刚才的界面:
这时候你就会发现里面多出来了你刚刚设置的路径,路径后面描述的“1 plug-ins available”则就是说的我们放置的Equinox的jar包。
继续点击finish,回到最开始的界面
这时候多出来了一个新的target platform,勾选上,然后确定。 [3] 
1.4.2 启动
打开菜单项Run->Run configurations…,在OSGi Framework项中,新建一个Runconfiguration:
这里面现在已经自动包含了Equinox的jar包了,点击Run,看看运行的效果:
Eclipse的控制台中也出现来了osgi的提示符,说明你已经成功启动了。
你可以试试刚才讲的那些命令,看看能输出些什么(比如上图中我输入了ss)。 [3] 
1.4.3 新建一个project
打开新建project的界面,选择Plug-in Project:
然后输入project的名字,TargetPlatform处选择an OSGi framework->Equinox或者standard都行,点击下一步:
这里实际上是对bundle的Manifest文件的设置,其中的ID就是Bundle-SymbolicName,Version就是bundle的版本号,下面还能决定是否定义BundleActivator,点击finish就创建了一个project
至此,开发环境已经建立完毕(这个project只是为了演示怎么建立,不会在接下来的内容中用到,可删之)。 [3] 
2 HelloWorld代码
现在可谓是万事具备,只欠Helloworld了。为了将OSGi框架的三个层次都涵盖到,这个Helloworld可能会比其他你见到的OSGi Helloworld程序要复杂一点点。如果对代码中的一些API感到生疏,记得回到之前的入门篇中找到对应的内容,这样对你理解代码会有帮助。里面的关键代码已经用黄色高亮显示。(出于篇幅考虑,代码中的import语句都省略)
2.1 HelloWorld的定义与实现
首先我们创建一个工程org.serc.helloworld,在这个工程里面,我们创建一个包含sayHello方法的接口,准备作为服务接口:
1
2
3
4
package org.serc.helloworld;
public interface Hello {
    void sayHello();
}
然后,对这个接口进行实现:
1
2
3
4
5
6
7
8
9
10
package org.serc.helloworld.impl;
public class HelloImpl implements Hello{
    final String helloString;
    public HelloImpl(String helloString){
        this.helloString= helloString;
    }
    public void sayHello(){
        System.out.println(this.helloString);
    }
}
这个类实现的sayHello所做的工作就是输出一个在对象构造的时候得到的helloString 字符串。
为了将这个接口暴露出来,我们需要在MANIFEST文件中加入如下条目:
1
Export-Package: org.serc.helloworld;version="1.0"
接下来,为了把这个服务注册到框架中,我们定义了一个Activator:
1
2
3
4
5
6
7
8
9
10
11
12
package org.serc.helloworld.activator;
public class Activator implements BundleActivator {
    private List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>();
    public void start(BundleContext ctx) {
        registrations.add(ctx.registerService(Hello.class.getName(),new HelloImpl("Hello, OSGi"), null));
    }
    public void stop(BundleContext ctx) {
        for(ServiceRegistration registration : registrations) {
        System.out.println("unregistering:"+ registration);
        registration.unregister();
    }
}
我们为这个HelloImpl传入了"Hello, OSGi"的字符串
为了让这个Activator能够工作,需要在MANIFEST文件中做如下定义:
1
Bundle-Activator:org.serc.helloworld.activator.Activator
这个bundle 最终的MANIFEST内容如下:
1
2
3
4
5
6
Bundle-ManifestVersion:2
Bundle-SymbolicName:org.serc.helloworld
Bundle-Version:1.0
Bundle-Activator:org.serc.helloworld.activator.Activator
Import-Package:org.osgi.framework
Export-Package: org.serc.helloworld;version="1.0"
2.2 获得并执行SayHello服务
创建一个工程org.serc.helloworld.client,创建一个叫HelloUser的BundleActivator,其中的start方法会获得接口为Hello的服务对象,并且通过这个对象来调用sayHello方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package org.serc.helloworld.client;
public class HelloUser implements BundleActivator {
    public void start(BundleContext ctx) {
        ServiceReference ref = ctx.getServiceReference(Hello.class.getName());
        if(ref != null) {
            Hello hello = null;
            try{
                hello= (Hello) ctx.getService(ref);
                if(hello != null)
                    hello.sayHello();
                else
                    System.out.println("Service:Hello---objectnull");
            }catch (RuntimeException e) {
                e.printStackTrace();
            }finally {
                ctx.ungetService(ref);
                hello= null;
            }
        }else {
            System.out.println("Service:Hello---notexists");
        }
    }
    public void stop(BundleContext ctx) throws Exception {
    }
}
为了获得Hello这个接口的定义,我们还需要在MANIFEST文件中import Hello所在的package:
1
2
3
4
5
Bundle-ManifestVersion:2
Bundle-SymbolicName:org.serc.helloworld.client
Bundle-Version:1.0
Bundle-Activator:org.serc.helloworld.client.HelloUser
Import-Package:org.serc.helloworld;version="[1.0,2.0)",org.osgi.framework
2.3 HelloWorld程序的流程
可能光看代码会比较不容易看清楚程序的执行流程,下图表示了这几个类的各个功能的相互依赖关系,整个关系从Hello接口的定义开始;然后到Hello接口被实现,得到HelloImpl;再到Activator将HelloImpl注册为框架中的一个服务,再到HelloUser通过与框架交互得到刚才注册的服务,并且使用这个服务从而输出字符串;最后一个可选流程是当我们stop org.serc.helloworld这个bundle的时候,程序会将之前注册的服务注销掉。
2.4 程序的执行
通过上面的工作,我们得到了两个自己定义的bundle:
org.serc.helloworld
org.serc.helloworld.client
现在打开Run configurations界面,我们会看见Bundles标签里面多出来了这两个bundle:
也就是说,OSGi框架在启动的时候,会自动install和start这2个bundle。
2.5 利用命令行查看程序执行过程中框架状态的变化
2.4其实只给出了一个结果,如果你还不太清楚这个结果具体是怎么出来的,那么这一节的内容应该能够帮助你更好的理解输出结果的过程。下面我们通过Equinox的一些命令行来一步一步安装和执行bundle,并且查看过程中框架的状态变化,来让你们搞清楚这个结果是怎么来的。
首先在Run configuration中取消两个helloworld bundle的自动启动:
然后点击Run,这时候就不会立即输出Hello, OSGi字符串了,现在我们先用“ss”命令查看bundle 的状态:
可见两个bundle并不是出于ACTIVE状态,说明并没有启动,现在我们执行“start 8”来启动org.serc.helloworld这个bundle
在用services命令查看当前已经注册的服务,我们会看到一大堆的系统服务中多出来如下一项服务
这显然是我们在start以后注册上去的,但是现在还没有任何一个bundle在使用这个服务。
接下来我们start 9号bundle,也就是我们用来调用服务的bundle
这时就输出了“Hello, OSGi”的字符串。
那么如果我们先启动9号bundle而不启动8号bundle会怎么样呢?大家可以试一试,因为我们在代码中已经对没有服务的异常情况做了处理,届时会有相应的输出。
我们先停止8号bundle
大家可以看见刚才注册的服务已经被注销了,现在我们执行refresh 11(也就是刚才的9号bundle)来重新执行其中BundleActivator的start方法
可见Hello服务已经不复存在了。从这里我们可以看出来,其实Bundle的启动顺序也是一个需要注意的环节,有时候你所定义的bundle是具有顺序敏感性的,必须要某些前置bundle启动了以后,后面的bundle才能正确启动。 [3] 
3 小结
这篇文档是入门篇的最后一章了,希望读者在花时间看完这4篇文档并且动手实践后能够有所收获,并且对OSGi框架的工作原理及其优势能有一个比较清晰的了解和认识。 [3] 

  

相关书籍

编辑

深入理解OSGi

1.《深入理解OSGi:Equinox原理、应用与最佳实践》
[4]
 

作者:周志明 & 谢小明,出版时间:2013-2-1.
内容简介:本书共14章,分4个部分。第一部分(第1章):走近OSGi,主要介绍了什么是OSGi以及为什么要使用OSGi。第二部分(第2~4章):OSGi规范与原理,对最新的OSGi

R5.0中的核心规范进行了全面的解读,首先讲解了OSGi模块的建立、描述、依赖关系的处理,然后讲解了Bundle的启动原理和调度管理,最后讲解了与本地及远程服务相关的内容。第三部分:OSGi服务与Equinox应用实践(第5~11章),不仅详细讲解了OSGi服务纲要规范和企业级规范中最常用的几个子规范和服务的技术细节,还通过一个基于Equinox的BBS案例演示了Equinox的使用方法,最重要的是还通过源码分析了Equinox关键功能的实现机制和原理。第四部分:最佳实践(第12~14章),总结了大量关于OSGi的最佳实践,包括从Bundle如何命名、模块划分、依赖关系处理到保持OSGi动态性、管理程序启动顺序、使用API基线管理模块版本等各方面的实践技巧,此外还介绍了Spring
DM的原理以及如何在OSGi环节中进行程序测试。

2.《Eclipse RCP与Spring OSGi:技术详解与最佳实践》
[5]

Eclipse RCP与Spring OSGi

作者:陆阳,出版时间:2013-1-1.
内容简介:全书共分3个部分:基础篇(第1~5章)详细介绍了与Eclipse
RCP相关的一系列核心概念、Eclipse
RCP开发环境的搭建,以及SWT、JFace、Forms、Nebula和WindowBuilder等Eclipse
RCP开发所常用的界面编程技术;高级篇(第6~12章)系统讲解了Eclipse RCP应用开发的基础知识、Eclipse
RCP软件产品各个组成部分的构建方法,以及Eclipse
RCP扩展的使用和扩展点的开发,掌握这些技术知识的读者将能构建一个结构完整的Eclipse
RCP软件,并解决软件开发过程中遇到的故障;实战篇(第13~15章)详细讲解了Eclipse RCP与Spring
OSGi框架、Hibernate ORM框架、JPA规范、Maven工具的整合,以及它与Java的模块化设计。
3.osgi中文社区
[6]
 
简介:由北京大学软件工程国家工程研究中心“软件协同研发支撑技术”组维护的一个模块化动态化开发技术的公益社区。初学者、进阶者和大牛都可以在这里分享技术和经验,自由的交流和学习,在贡献和收获中充实自己。为更多的学生、研究者和从业者提供更广阔的交流平台。
词条图册
更多图册
词条图片(26)
 
参考资料

OSGI框架—HelloWorld小实例的更多相关文章

  1. ssh框架的小实例(用户登录)

    刚学SSH框架写一个小实例,以便以后查看: 本案例简单的实现一个用户登录: 数据库方面就不写了,自己领悟吧!哈哈(根据user.hbm.xml文件就知道了) 我们一般可以创建下面几个包,什么意思呢,自 ...

  2. Spring3.0 与 MyBatis框架 整合小实例

    本文将在Eclipse开发环境下,采用Spring MVC + Spring + MyBatis + Maven + Log4J 框架搭建一个Java web 项目. 1. 环境准备: 1.1 创建数 ...

  3. [置顶] Cocos2d-x 实例源码分析之二 小实例的主框架

    这篇文章是分析第一个小实例ActionTest的源码.其实所有实例程序的结构都是一样的,只有特定方法里的代码不同,大的框架都是一样的.也就是说看完这篇文章你就可以自己开始分析其他源码了. 废话不多说, ...

  4. 从一个简单的小实例分析JSP+Servelt与JSP+Struts2框架的区别

    最近在学struts2,struts2相比以前的JSP+Servlet,在处理流程上的更简单,我们就一个小实例来具体分析一下. 实例内容如下: 实现一个简单的注册页面包括:用户名.密码.重复密码.年龄 ...

  5. MVC框架模式技术实例(用到隐藏帧、json、仿Ajax、Dom4j、jstl、el等)

    前言: 刚刚学完了MVC,根据自己的感悟和理解写了一个小项目. 完全按照MVC模式,后面有一个MVC的理解示意图. 用MVC模式重新完成了联系人的管理系统: 用户需求: 多用户系统,提供用户注册.登录 ...

  6. Spring初识(通过小实例清晰认识Spring)

    1.spring架构: spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,iba ...

  7. spring+mybatis之声明式事务管理初识(小实例)

    前几篇的文章都只是初步学习spring和mybatis框架,所写的实例也都非常简单,所进行的数据访问控制也都很简单,没有加入事务管理.这篇文章将初步接触事务管理. 1.事务管理 理解事务管理之前,先通 ...

  8. Springboot Application 集成 OSGI 框架开发

    内容来源:https://www.ibm.com/developerworks/cn/java/j-springboot-application-integrated-osgi-framework-d ...

  9. Eclipse IDE下的Spring框架使用简单实例

    Eclipse IDE下的Spring框架使用简单实例 1 准备Java jdk安装. Eclipse软件安装.根据系统安装32/64版本,选择Eclipse IDE for Java Develop ...

随机推荐

  1. spring-boot 集合mybatis 的分页查询

    spring-boot 集合mybatis 的github分页查询 一.依赖包 <!-- mysql 数据库驱动. --> <dependency> <groupId&g ...

  2. android 面试

    2. 横竖屏切换时候 activity 的生命周期1. 不设置 Activity 的 android:configChanges 时 , 切屏会重新调用各个生命周期 , 切横屏时会执行一次 , 切竖屏 ...

  3. lua工具库penlight--08额外的库(一)

    额外的库 在这一节中的库不再被认为是Penlight的核心部分,但在需要时,仍提供专门的功能. 简单的输入的模式 Lua 的字符串模式匹配是非常强大,通常您将不需要传统的正则表达式库.即便如此,有时  ...

  4. gpio 灯的对应关系

    1 点灯验证通过:   GPIO160     TX1-LED GPIO161     RX1-LED   GPIO163     TX2-LED GPIO164     RX2-LED   GPIO ...

  5. Linux crontab 实现每秒执行

    Linux crontab 实现每秒执行 linux crontab 命令,最小的执行时间是一分钟.如需要在小于一分钟内重复执行,可以有两个方法实现. 1.使用延时来实现每N秒执行 创建一个php做执 ...

  6. make: *** [sapi/cli/php] Error 1 解决办法

    make: *** [sapi/cli/php] Error 1 一:考虑过make clean,问题依然 二:(采取此方法后出现启动apache报错:/usr/local/apache2/modul ...

  7. python文件编码说明 coding=utf-8

    python 支持3种编码声明,一般常用能见到下面两种 1.# -*- coding: utf-8 -*- 这种写法是为了兼容Emacs的编码声明 2.短一点,但Emacs不能用# coding=ut ...

  8. javax.Servlet的包中,属于类的是。(选择1项)

    javax.Servlet的包中,属于类的是.(选择1项) A.Servlet B.GenericServlet C.ServletRequest D.ServletContext 解答:B Serv ...

  9. Mac OSX使用 XAMPP path 下的php

    修改-/.bash_profile文件或.zshrc文件 export XAMPP_HOME=/Applications/XAMPP export PATH=${XAMPP_HOME}/bin:${P ...

  10. 通过公网IP主机建立ssh隧道

    环境描述 hostA: 有公网IP的linux主机 hostB: 私有路由器后端无公网IPlinux主机,能够ssh连接到hostA hostC: 个人pc机 隧道创建步骤 step1 在hostB上 ...