阅读更多

1顶
0踩

编程语言

转载新闻 使用Python编写虚拟机解释器

2015-10-30 10:53 by 副主编 mengyidan1988 评论(1) 有5832人浏览
Stack Machine 本身并没有任何的寄存器,它将所需要处理的值全部放入堆栈中而后进行处理。Stack Machine 虽然简单但是却十分强大,这也是为神马 Python,Java,PostScript,Forth 和其他语言都选择它作为自己的虚拟机的原因。

首先,我们先来谈谈堆栈。我们需要一个指令指针栈用于保存返回地址。这样当我们调用了一个子例程(比如调用一个函数)的时候我们就能够返回到我们开始调用的地方了。我们可以使用自修改代码(self-modifying code)来做这件事,恰如 Donald Knuth 发起的 MIX 所做的那样。但是如果这么做的话你不得不自己维护堆栈从而保证递归能正常工作。在这篇文章中,我并不会真正的实现子例程调用,但是要实现它其实并不难(可以考虑把实现它当成练习)。

有了堆栈之后你会省很多事儿。举个例子来说,考虑这样一个表达式(2+3)*4。在 Stack Machine 上与这个表达式等价的代码为 2 3 + 4 *。首先,将 2 和 3 推入堆栈中,接下来的是操作符 +,此时让堆栈弹出这两个数值,再把它两加合之后的结果重新入栈。然后将 4 入堆,而后让堆栈弹出两个数值,再把他们相乘之后的结果重新入栈。多么简单啊!

让我们开始写一个简单的堆栈类吧。让这个类继承 collections.deque:
from collections import deque

class Stack(deque):
push = deque.append

def top(self):
    return self[-1]

现在我们有了 push、pop 和 top 这三个方法。top 方法用于查看栈顶元素。

接下来,我们实现虚拟机这个类。在虚拟机中我们需要两个堆栈以及一些内存空间来存储程序本身(译者注:这里的程序请结合下文理解)。得益于 Pyhton 的动态类型我们可以往 list 中放入任何类型。唯一的问题是我们无法区分出哪些是字符串哪些是内置函数。正确的做法是只将真正的 Python 函数放入 list 中。我可能会在将来实现这一点。

我们同时还需要一个指令指针指向程序中下一个要执行的代码。
class Machine:
def __init__(self, code):
    self.data_stack = Stack()
    self.return_addr_stack = Stack()
    self.instruction_pointer = 0
    self.code = code

这时候我们增加一些方便使用的函数省得以后多敲键盘。
def pop(self):
    return self.data_stack.pop()

def push(self, value):
    self.data_stack.push(value)

def top(self):
    return self.data_stack.top()

然后我们增加一个 dispatch 函数来完成每一个操作码做的事儿(我们并不是真正的使用操作码,只是动态展开它,你懂的)。首先,增加一个解释器所必须的循环:
def run(self):
    while self.instruction_pointer < len(self.code):
        opcode = self.code[self.instruction_pointer]
        self.instruction_pointer += 1
        self.dispatch(opcode)

诚如您所见的,这货只好好的做一件事儿,即获取下一条指令,让指令指针执自增,然后根据操作码分别处理。dispatch 函数的代码稍微长了一点。
def dispatch(self, op):
    dispatch_map = {
        "%":        self.mod,
        "*":        self.mul,
        "+":        self.plus,
        "-":        self.minus,
        "/":        self.div,
        "==":       self.eq,
        "cast_int": self.cast_int,
        "cast_str": self.cast_str,
        "drop":     self.drop,
        "dup":      self.dup,
        "if":       self.if_stmt,
        "jmp":      self.jmp,
        "over":     self.over,
        "print":    self.print_,
        "println":  self.println,
        "read":     self.read,
        "stack":    self.dump_stack,
        "swap":     self.swap,
    }

    if op in dispatch_map:
        dispatch_map[op]()
    elif isinstance(op, int):
        # push numbers on the data stack
        self.push(op)
    elif isinstance(op, str) and op[0]==op[-1]=='"':
        # push quoted strings on the data stack
        self.push(op[1:-1])
    else:
        raise RuntimeError("Unknown opcode: '%s'" % op)

基本上,这段代码只是根据操作码查找是都有对应的处理函数,例如 * 对应 self.mul,drop 对应 self.drop,dup对应 self.dup。顺便说一句,你在这里看到的这段代码其实本质上就是简单版的 Forth。而且,Forth 语言还是值得您看看的。

总之捏,它一但发现操作码是 * 的话就直接调用 self.mul 并执行它。就像这样:
def mul(self):
    self.push(self.pop() * self.pop())

其他的函数也是类似这样的。如果我们在 dispatch_map 中查找不到相应操作函数,我们首先检查他是不是数字类型,如果是的话直接入栈;如果是被引号括起来的字符串的话也是同样处理--直接入栈。

截止现在,恭喜你,一个虚拟机就完成了。

让我们定义更多的操作,然后使用我们刚完成的虚拟机和 p-code 语言来写程序。
# Allow to use "print" as a name for our own method:
from __future__ import print_function

# ...
def plus(self):
    self.push(self.pop() + self.pop())

def minus(self):
    last = self.pop()
    self.push(self.pop() - last)

def mul(self):
    self.push(self.pop() * self.pop())

def div(self):
    last = self.pop()
    self.push(self.pop() / last)

def print(self):
    sys.stdout.write(str(self.pop()))
    sys.stdout.flush()

def println(self):
    sys.stdout.write("%s\n" % self.pop())
    sys.stdout.flush()

让我们用我们的虚拟机写个与 print((2+3)*4) 等同效果的例子。

Machine([2, 3, "+", 4, "*", "println"]).run() 你可以试着运行它。

现在引入一个新的操作 jump, 即 go-to 操作
def jmp(self):
    addr = self.pop()
    if isinstance(addr, int) and 0 <= addr < len(self.code):
        self.instruction_pointer = addr
    else:
        raise RuntimeError("JMP address must be a valid integer.")

它只改变指令指针的值。我们再看看分支跳转是怎么做的。
def if_stmt(self):
    false_clause = self.pop()
    true_clause = self.pop()
    test = self.pop()
    self.push(true_clause if test else false_clause)

这同样也是很直白的。如果你想要添加一个条件跳转,你只要简单的执行 test-value true-value false-value IF JMP 就可以了.(分支处理是很常见的操作,许多虚拟机都提供类似 JNE 这样的操作。JNE 是 jump if not equal 的缩写)。

下面的程序要求使用者输入两个数字,然后打印出他们的和和乘积。
Machine([
'"Enter a number: "', "print", "read", "cast_int",
'"Enter another number: "', "print", "read", "cast_int",
"over", "over",
'"Their sum is: "', "print", "+", "println",
'"Their product is: "', "print", "*", "println"
]).run()

over、read 和 cast_int 这三个操作是长这样滴:
def cast_int(self):
    self.push(int(self.pop()))

def over(self):
    b = self.pop()
    a = self.pop()
    self.push(a)
    self.push(b)
    self.push(a)

def read(self):
    self.push(raw_input())

以下这一段程序要求使用者输入一个数字,然后打印出这个数字是奇数还是偶数。
Machine([
'"Enter a number: "', "print", "read", "cast_int",
'"The number "', "print", "dup", "print", '" is "', "print",
2, "%", 0, "==", '"even."', '"odd."', "if", "println",
0, "jmp" # loop forever!
]).run()

这里有个小练习给你去实现:增加 call 和 return 这两个操作码。call 操作码将会做如下事情 :将当前地址推入返回堆栈中,然后调用 self.jmp()。return 操作码将会做如下事情:返回堆栈弹栈,将弹栈出来元素的值赋予指令指针(这个值可以让你跳转回去或者从 call 调用中返回)。当你完成这两个命令,那么你的虚拟机就可以调用子例程了。

一个简单的解析器

创造一个模仿上述程序的小型语言。我们将把它编译成我们的机器码。
 import tokenize
 from StringIO import StringIO

# ...

def parse(text):
tokens =   tokenize.generate_tokens(StringIO(text).readline)
for toknum, tokval, _, _, _ in tokens:
    if toknum == tokenize.NUMBER:
        yield int(tokval)
    elif toknum in [tokenize.OP, tokenize.STRING, tokenize.NAME]:
        yield tokval
    elif toknum == tokenize.ENDMARKER:
        break
    else:
        raise RuntimeError("Unknown token %s: '%s'" %
                (tokenize.tok_name[toknum], tokval))

一个简单的优化:常量折叠

常量折叠(Constant folding)是窥孔优化(peephole optimization)的一个例子,也即是说再在编译期间可以针对某些明显的代码片段做些预计算的工作。比如,对于涉及到常量的数学表达式例如 2 3 +就可以很轻松的实现这种优化。
def constant_fold(code):
"""Constant-folds simple mathematical expressions like 2 3 + to 5."""
while True:
    # Find two consecutive numbers and an arithmetic operator
    for i, (a, b, op) in enumerate(zip(code, code[1:], code[2:])):
        if isinstance(a, int) and isinstance(b, int) \
                and op in {"+", "-", "*", "/"}:
            m = Machine((a, b, op))
            m.run()
            code[i:i+3] = [m.top()]
            print("Constant-folded %s%s%s to %s" % (a,op,b,m.top()))
            break
    else:
        break
return code

采用常量折叠遇到唯一问题就是我们不得不更新跳转地址,但在很多情况这是很难办到的(例如:test cast_int jmp)。针对这个问题有很多解决方法,其中一个简单的方法就是只允许跳转到程序中的命名标签上,然后在优化之后解析出他们真正的地址。

如果你实现了 Forth words,也即函数,你可以做更多的优化,比如删除可能永远不会被用到的程序代码(dead code elimination)

REPL

我们可以创造一个简单的 PERL,就像这样
def repl():
print('Hit CTRL+D or type "exit" to quit.')

while True:
    try:
        source = raw_input("> ")
        code = list(parse(source))
        code = constant_fold(code)
        Machine(code).run()
    except (RuntimeError, IndexError) as e:
        print("IndexError: %s" % e)
    except KeyboardInterrupt:
        print("\nKeyboardInterrupt")

用一些简单的程序来测试我们的 REPL
> 2 3 + 4 * println
Constant-folded 2+3 to 5
Constant-folded 5*4 to 20
20
> 12 dup * println
144
> "Hello, world!" dup println println
Hello, world!
Hello, world!
你可以看到,常量折叠看起来运转正常。在第一个例子中,它把整个程序优化成这样 20 println。


下一步

当你添加完 call 和 return 之后,你便可以让使用者定义自己的函数了。在Forth 中函数被称为 words,他们以冒号开头紧接着是名字然后以分号结束。例如,一个整数平方的 word 是长这样滴
: square dup * ;

实际上,你可以试试把这一段放在程序中,比如 Gforth
$ gforth
Gforth 0.7.3, Copyright (C) 1995-2008 Free Software Foundation, Inc.
Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
Type `bye' to exit
: square dup * ;  ok
12 square . 144  ok

你可以在解析器中通过发现 : 来支持这一点。一旦你发现一个冒号,你必须记录下它的名字及其地址(比如:在程序中的位置)然后把他们插入到符号表(symbol table)中。简单起见,你甚至可以把整个函数的代码(包括分号)放在字典中,譬如:
symbol_table = {
"square": ["dup", "*"]
# ...
    }

当你完成了解析的工作,你可以连接你的程序:遍历整个主程序并且在符号表中寻找自定义函数的地方。一旦你找到一个并且它没有在主程序的后面出现,那么你可以把它附加到主程序的后面。然后用 <address> call 替换掉 square,这里的 <address> 是函数插入的地址。

为了保证程序能正常执行,你应该考虑剔除 jmp 操作。否则的话,你不得不解析它们。它确实能执行,但是你得按照用户编写程序的顺序保存它们。举例来说,你想在子例程之间移动,你要格外小心。你可能需要添加 exit 函数用于停止程序(可能需要告诉操作系统返回值),这样主程序就不会继续执行以至于跑到子例程中。

实际上,一个好的程序空间布局很有可能把主程序当成一个名为 main 的子例程。或者由你决定搞成什么样子。

如您所见,这一切都是很有趣的,而且通过这一过程你也学会了很多关于代码生成、链接、程序空间布局相关的知识。

更多能做的事儿

你可以使用 Python 字节码生成库来尝试将虚拟机代码为原生的 Python 字节码。或者用 Java 实现运行在 JVM 上面,这样你就可以自由使用 JITing。

同样的,你也可以尝试下register machine。你可以尝试用栈帧(stack frames)实现调用栈(call stack),并基于此建立调用会话。

最后,如果你不喜欢类似 Forth 这样的语言,你可以创造运行于这个虚拟机之上的自定义语言。譬如,你可以把类似 (2+3)*4 这样的中缀表达式转化成 2 3 + 4 * 然后生成代码。你也可以允许 C 风格的代码块 { ... } 这样的话,语句 if ( test ) { ... } else { ... } 将会被翻译成
<true/false test>
<address of true block>
<address of false block>
if
jmp

<true block>
<address of end of entire if-statement> jmp

<false block>
<address of end of entire if-statement> jmp

例子,
Address  Code
-------  ----
 0       2 3 >
 3       7        # Address of true-block
 4       11       # Address of false-block
 5       if
 6       jmp      # Conditional jump based on test

# True-block

7     "Two is greater than three."    
8       println
9       15       # Continue main program
10       jmp

# False-block ("else { ... }")
11       "Two is less than three."
12       println
13       15       # Continue main program
14       jmp

# If-statement finished, main program continues here
15       ...

对了,你还需要添加比较操作符 != < <= > >=。

我已经在我的 C++ stack machine 实现了这些东东,你可以参考下。

我已经把这里呈现出来的代码搞成了个项目 Crianza,它使用了更多的优化和实验性质的模型来吧程序编译成 Python 字节码。

祝好运!

完整的代码

下面是全部的代码,兼容 Python 2 和 Python 3

你可以通过这里得到它。
#!/usr/bin/env python
# coding: utf-8
"""
A simple VM interpreter.
Code from the post at http://csl.name/post/vm/
This version should work on both Python 2 and 3.
"""
from __future__ import print_function
from collections import deque
from io import StringIO
import sys
import tokenize
def get_input(*args, **kw):
"""Read a string from standard input."""
if sys.version[0] == "2":
    return raw_input(*args, **kw)
else:
    return input(*args, **kw)
class Stack(deque):
push = deque.append
def top(self):
    return self[-1]
class Machine:
def __init__(self, code):
    self.data_stack = Stack()
    self.return_stack = Stack()
    self.instruction_pointer = 0
    self.code = code
def pop(self):
    return self.data_stack.pop()
def push(self, value):
    self.data_stack.push(value)
def top(self):
    return self.data_stack.top()
def run(self):
    while self.instruction_pointer < len(self.code):
        opcode = self.code[self.instruction_pointer]
        self.instruction_pointer += 1
        self.dispatch(opcode)
def dispatch(self, op):
    dispatch_map = {
        "%":        self.mod,
        "*":        self.mul,
        "+":        self.plus,
        "-":        self.minus,
        "/":        self.div,
        "==":       self.eq,
        "cast_int": self.cast_int,
        "cast_str": self.cast_str,
        "drop":     self.drop,
        "dup":      self.dup,
        "exit":     self.exit,
        "if":       self.if_stmt,
        "jmp":      self.jmp,
        "over":     self.over,
        "print":    self.print,
        "println":  self.println,
        "read":     self.read,
        "stack":    self.dump_stack,
        "swap":     self.swap,
    }
    if op in dispatch_map:
        dispatch_map[op]()
    elif isinstance(op, int):
        self.push(op) # push numbers on stack
    elif isinstance(op, str) and op[0]==op[-1]=='"':
        self.push(op[1:-1]) # push quoted strings on stack
    else:
        raise RuntimeError("Unknown opcode: '%s'" % op)
# OPERATIONS FOLLOW:
def plus(self):
    self.push(self.pop() + self.pop())
def exit(self):
    sys.exit(0)
def minus(self):
    last = self.pop()
    self.push(self.pop() - last)
def mul(self):
    self.push(self.pop() * self.pop())
def div(self):
    last = self.pop()
    self.push(self.pop() / last)
def mod(self):
    last = self.pop()
    self.push(self.pop() % last)
def dup(self):
    self.push(self.top())
def over(self):
    b = self.pop()
    a = self.pop()
    self.push(a)
    self.push(b)
    self.push(a)
def drop(self):    self.pop()

def swap(self):
    b = self.pop()
    a = self.pop()
    self.push(b)
    self.push(a)
def print(self):
    sys.stdout.write(str(self.pop()))
    sys.stdout.flush()
def println(self):
    sys.stdout.write("%s\n" % self.pop())
    sys.stdout.flush()
def read(self):
    self.push(get_input())
def cast_int(self):
    self.push(int(self.pop()))
def cast_str(self):
    self.push(str(self.pop()))
def eq(self):
    self.push(self.pop() == self.pop())
def if_stmt(self):
    false_clause = self.pop()
    true_clause = self.pop()
    test = self.pop()
    self.push(true_clause if test else false_clause)
def jmp(self):
    addr = self.pop()
    if isinstance(addr, int) and 0 <= addr < len(self.code):
        self.instruction_pointer = addr
    else:
        raise RuntimeError("JMP address must be a valid integer.")

def dump_stack(self):
    print("Data stack (top first):")

    for v in reversed(self.data_stack):
        print(" - type %s, value '%s'" % (type(v), v))
def parse(text):
# Note that the tokenizer module is intended for parsing Python source
# code, so if you're going to expand on the parser, you may have to use
# another tokenizer.
if sys.version[0] == "2":
    stream = StringIO(unicode(text))
else:
    stream = StringIO(text)
tokens = tokenize.generate_tokens(stream.readline)
for toknum, tokval, _, _, _ in tokens:
    if toknum == tokenize.NUMBER:
        yield int(tokval)
    elif toknum in [tokenize.OP, tokenize.STRING, tokenize.NAME]:
        yield tokval
    elif toknum == tokenize.ENDMARKER:
        break
    else:
        raise RuntimeError("Unknown token %s: '%s'" %
                (tokenize.tok_name[toknum], tokval))
def constant_fold(code):
"""Constant-folds simple mathematical expressions like 2 3 + to 5."""
while True:
    # Find two consecutive numbers and an arithmetic operator
    for i, (a, b, op) in enumerate(zip(code, code[1:], code[2:])):
        if isinstance(a, int) and isinstance(b, int) \
                and op in {"+", "-", "*", "/"}:
            m = Machine((a, b, op))
            m.run()
            code[i:i+3] = [m.top()]
            print("Constant-folded %s%s%s to %s" %     (a,op,b,m.top()))
            break
        else:
            break
        return code
def repl():
print('Hit CTRL+D or type "exit" to quit.')

while True:
    try:
        source = get_input("> ")
        code = list(parse(source))
        code = constant_fold(code)
        Machine(code).run()
    except (RuntimeError, IndexError) as e:
        print("IndexError: %s" % e)
    except KeyboardInterrupt:
        print("\nKeyboardInterrupt")
def test(code = [2, 3, "+", 5, "*", "println"]):
print("Code before optimization: %s" % str(code))
optimized = constant_fold(code)
print("Code after optimization: %s" % str(optimized))
print("Stack after running original program:")
a = Machine(code)
a.run()
a.dump_stack()
print("Stack after running optimized program:")
b = Machine(optimized)
b.run()
b.dump_stack()
result = a.data_stack == b.data_stack
print("Result: %s" % ("OK" if result else "FAIL"))
return result
def examples():
print("** Program 1: Runs the code for `print((2+3)*4)`")
Machine([2, 3, "+", 4, "*", "println"]).run()

print("\n** Program 2: Ask for numbers, computes sum and product.")
Machine([
    '"Enter a number: "', "print", "read", "cast_int",
    '"Enter another number: "', "print", "read", "cast_int",
    "over", "over",
    '"Their sum is: "', "print", "+", "println",
    '"Their product is: "', "print", "*", "println"
]).run()
print("\n** Program 3: Shows branching and looping (use CTRL+D to exit).")
Machine([
    '"Enter a number: "', "print", "read", "cast_int",
    '"The number "', "print", "dup", "print", '" is "', "print",
    2, "%", 0, "==", '"even."', '"odd."', "if", "println",
    0, "jmp" # loop forever!
]).run()
if __name__ == "__main__":
try:
    if len(sys.argv) > 1:
        cmd = sys.argv[1]
        if cmd == "repl":
            repl()
        elif cmd == "test":
            test()
            examples()
        else:
            print("Commands: repl, test")
    else:
        repl()
except EOFError:
    print("")


原文地址:Making a simple VM interpreter in Python
本文系OneAPM工程师编译整理。OneAPM是中国基础软件领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和SQL语句的实时抓取。想阅读更多技术文章,请访问OneAPM官方技术博客
来自: OneAPM
1
0
评论 共 1 条 请登录后发表评论
1 楼 mangguo 2015-11-03 13:30
真是太强悍。

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • php cookie被赋值,php删除cookie反倒被赋值为deleted 解决办法

    php删除cookie反倒被赋值为deleted 解决办法原因是:如果遇到用户客户端的电脑本地时间 比当前服务器时间少1年以上,IE浏览器就会出现这种情况。这种情况会致使用户永远点击退出退出不了。因为username被赋值为...

  • 101件 Mozilla 浏览器能而 IE 不能做的事

    至少,理论上,IE 有类似的功能,它做的是为离线浏览执行同步化内容。它只能一天检查一次,并且在页面改变时不会用更换图标或寄电子邮件给你。Mozilla 可以在指定的日期或时间间隔内检查有无内容更新。 (译注:IE ...

  • Ext JS的4.1.0的RC 1的发行说明

    Ext JS的 - JavaScript框架 Ext JS的4.1.0的RC 1的发行说明 ...EXTJSIV-5129 按钮不坚持宽度设置在IE9 图表 EXTJSIV-4416 图表标签 EXTJSIV-5459 设置在渲染隐藏的标记,有没有效果,图

  • 关于Ext formPanel.getForm().submit()方法与standardSubmit属性的一些事

    这与Ext.ajax.request方法的返回依据是不同的。   另外还有很重要的一点,就是上述红色字体所标注的地方 Note: this is ignored when using the standardSubmit option.   也就是说,我们定义的...

  • ExtJS2 0开发与实践笔记 2 ——Ext中的Layout

    ExtJS2 0开发与实践笔记 2 ——Ext中的Layout

  • ExtJS2.0开发与实践笔记[2]——Ext中的Layout

    而在Ext中同样提供了自己的布局实现,以简化web界面的开发与定制。 如下图所示,Ext的Layout可分解为东、西、南、北、中5个基本区域。在ExtJS2.0实现中,我们可以写成如下代码样式。ExtJS2.0配置方法如下:初识...

  • Ext的 Layout布局

    而在Ext中同样提供了自己的布局实现,以简化web界面的开发与定制。如下图所示,Ext的Layout可分解为东、西、南、北、中5个基本区域。 在ExtJS2.0实现中,我们可以写成如下代码样式。ExtJS2.0配置方法如下:初识...

  • Ext的Layout布局

    而在Ext中同样提供了自己的布局实现,以简化web界面的开发与定制。如下图所示,Ext的Layout可分解为东、西、南、北、中5个基本区域。在ExtJS2.0实现中,我们可以写成如下代码样式。ExtJS2.0配置方法如下:初识...

  • 自动化工具大全

    这里对自动化工具进行了汇总简介,给在自动化路上迷茫,不知道学那款软件的你们,综合自己所掌握的知识,选择最适合自己的自动化工具

  • 【全栈接口测试进阶系列教程】入门到入职的jmeter接口测试工具实战,接口测试步骤,正则表达式jsonpath,断言,接口加密,beanshell,jdbc,jmeter+ant+jenkins

    Apache JMeter 是 Apache 组织基于 Java 开发的压力测试工具,用于对软件做压力测试。JMeter还可以做接口测试和性能测试。(一)参数化1、实现方式:函数方式,文件方式2、参数调用格式:统一格式:${参数名}(二)自动...

  • 一文读懂 Jmeter - 你以为Jmeter只能用来做压力测试?

    但可扩展) 支持(很强,图形报表一直是商业工具的强项) 测试的逻辑控制 支持 支持 服务器硬件资源监控 支持 支持 功能测试 支持 不支持 Jmeter常用组件和概念介绍 测试计划:是根、是老大。所有的内容都是基于这个...

  • FireFoxy有用的插件和使用技巧

    我自己平时用到的插件有将近30种,推荐一些实用性较强的,有部分不支持2.0版。仅供参考。推荐一个绿色版的下载。 Adblock Plus:防止弹出广告。包括网页上的广告条、广告图片等。并且可以利用右键相关选项添加那些你...

  • 基于Android 7.0与Android Studio的安卓学习.zip

    Android是一种基于Linux内核(不包含GNU组件)的自由及开放源代码的移动操作系统,主要应用于移动设备,如智能手机和平板电脑。该系统最初由安迪·鲁宾开发,后被Google公司收购并注资,随后与多家硬件制造商、软件开发商及电信营运商共同研发改良。 Android操作系统的特点包括: 开放源代码:Android系统采用开放源代码模式,允许开发者自由访问、修改和定制操作系统,这促进了技术的创新和发展,使得Android系统具有高度的灵活性和可定制性。 多任务处理:Android允许用户同时运行多个应用程序,并且可以轻松地在不同应用程序之间切换,提高了效率和便利性。 丰富的应用生态系统:Android系统拥有庞大的应用程序生态系统,用户可以从Google Play商店或其他第三方应用市场下载和安装各种各样的应用程序,满足各种需求。 可定制性:Android操作系统可以根据用户的个人喜好进行定制,用户可以更改主题、小部件和图标等,以使其界面更符合个人风格和偏好。 多种设备支持:Android操作系统可以运行在多种不同类型的设备上,包括手机、平板电脑、智能电视、汽车导航系统等。 此外,Android系统还有一些常见的问题,如应用崩溃、电池耗电过快、Wi-Fi连接问题、存储空间不足、更新问题等。针对这些问题,用户可以尝试一些基本的解决方法,如清除应用缓存和数据、降低屏幕亮度、关闭没有使用的连接和传感器、限制后台运行的应用、删除不需要的文件和应用等。 随着Android系统的不断发展,其功能和性能也在不断提升。例如,最新的Android版本引入了更多的安全性和隐私保护功能,以及更流畅的用户界面和更强大的性能。此外,Android系统也在不断探索新的应用场景,如智能家居、虚拟现实、人工智能等领域。 总之,Android系统是一种功能强大、灵活可定制、拥有丰富应用生态系统的移动操作系统,在全球范围内拥有广泛的用户基础。

  • node-v4.6.1-sunos-x86.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

  • node-v6.3.0-linux-armv7l.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

  • node-v6.9.2-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

  • 甲壳虫ADB助手v1.3.0@高级版.apk

    甲壳虫ADB助手v1.3.0@高级版.apk

  • 机房工程 能源管理 综合运维 大样图.rar

    新旧理念对比:在信息化建设中,数据中心基础设施至关重要,因控制系统数字化,数据中心承载核心组件和数据。目前数据中心资源利用率低,面临节能减排挑战。新型绿色数据中心与传统相比,更注重节能、模块化设计、技术应用和精细化管理,以提高能源效率和减少环境影响。 机房详细设计:省数据机房作为业务处理和数据中心,承担通讯枢纽角色,需保证安全、可靠运行和良好工作环境。依据国家相关政策和标准,设计绿色数据中心机房,包括建筑照明、采暖通风、计算机房用空调机、民用建筑热工设计等规范。 机房设计思路:数据中心发展为高性能计算机集中地,面临能耗效率和资源整合问题。绿色数据中心集成网络设施、服务器、UPS供电等,采用模块化设计,提升机房利用率。 机房总体划分:根据需求和功能,机房划分为主机房区、支持区和辅助区。主机房区包括技侦、刑侦、电话、网监机房等;支持区包括动力电源、发电机房、UPS电源机房等;辅助区包括休息区、监控室等。设计考虑微模块化、空间结构、数据路由和信息点。 机房详细设计:IDC机房建设包括装饰装修、电气系统、弱电集成。装修原则注重防尘、防火、保温、防水和色彩设计。材料选择原则为防火、防水、防静电、不起尘且环保。系统概述包括供配电系统、UPS不间断电源、照明与应急照明、防雷接地等。 数据中心布线系统:基于GB50174-2008和TIA/EIA-942标准,采用光纤与双绞线结合,线缆选型为CMP级电缆,OFNP或OFCP光缆。布线设计采用卡博菲式网格桥架,减轻承重压力,节省空间。 空调新风系统:新型系统采用模块化组合,提高冷气流利用率,智能温感控制,降低PUE值,节能高效。与传统系统相比,具有显著节能优势。 消防自动报警及气体灭火系统:设计为有管网式,包括感烟、感温探测器,声光报警器,紧急启停按钮等。系统实现自动报警、联动控制和手动远程启动。 集中监控管理系统:集成机房动力、环境、安防和服务器系统资源监控,基于WEB远程访问,具备日志和报表管理功能,适合集成监控。 KVM监控管理系统:安装主服务器和交换机,实现全域用户管理和日志服务。系统支持多厂商模拟KVM级联,通过主服务器集群实现统一管理和访问控制。

  • node-v6.6.0-linux-arm64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

Global site tag (gtag.js) - Google Analytics