3这篇文章,build对class文件进行更新

作者: 前端  发布:2019-12-28

NetBeans是一个集多种编译语言和脚本语言的程序平台,我们在写java代码时每每修改都得build class觉得很烦。现在又有好消息:最新的netbeans 6.5daily版已经让我们可以用save 来替代 build对class文件进行更新,相信很多喜爱netBeans的程序员都很开心。顺便提一下,netBeans对python、PHP、groove、scala支持得也越来越好,现在很希望netBeans加入像支持rail一样强的支持其它脚本语言的框架,如python 的django。那就更完美了。

第四部分

看看类的 __get__和其他类似的两个__getattr__、__getattribute__这些都很容易百度到的,但是为什么一个Function中会有出现一个descriptor 的__get__呢。
看下面代码。

class Acfun:
    def acfun(self):
        print('acfun!')

print(Acfun.acfun)#<function Acfun.acfun at 0x0148BFA8>
print(Acfun().acfun)
#<bound method Acfun.acfun of <__main__.Acfun object at 0x006FF5D0>>

我用的是python3,网上很多的文章都有关于unbound和bound的讲解,但是py3中以及不存在unbound了。

class Acfun:
    def acfun(self):
        print('acfun!')
print(Acfun().acfun.__get__)
#<method-wrapper '__get__' of function object at 0x01FF5030>
print(Acfun().acfun.__get__())
'''
Traceback (most recent call last):
  File "D:/PythonProject/VirtualMachine/test.py", line 18, in <module>
    print(Acfun().acfun.__get__())
TypeError:  expected at least 1 arguments, got 0
'''

看到了么,class的函数也就是方法都有一个__get__属性,这就是之前我们的Function中要有一个__get__方法的理由了。
再来看看这个__get__方法有什么用好了。

class Acfun:
    def acfun(self):
        print('acfun!')
print(Acfun().acfun.__get__(Acfun())())
#acfun!
#None

不知道你是否有了解过python的偏函数的概念呢,它们就类似于偏函数一样。其实调用的bound函数都是经过了method-wrapper包装,然后进行了绑定绑定的对象是自己的实例。
这个包装是默认的,同时另外两个包装(classmethod、staticmethod)是显式的。因此,你必须要有一个实例传进去要不然就报错了。
还有普通的函数使用__get__是没用的,但是如果你足够无聊的话可以向上面调用方法一样调用一个函数。
同时关于我们这个__get__方法返回的Method类。
method就是方法,function是函数。在python里面这两个概念还是有所不同的,class中的functon就是method了。如上文所述没有被绑定的function就只是function而被绑定的就是method。要绑定方法就是这样的。

class Acfun:
    def acfun(s):
        print('acfun!')
print(Acfun.acfun.__get__(Acfun()))
#<bound method Acfun.acfun of <__main__.Acfun object at 0x0039F770>>

好了,它现在是方法了。
好了,我已经实现了函数了,要对frame做改动,在进行切换的时候记得保存好offset的值,我这里的做法和文章中有些不同。还有要记得把返回值放过去。
我在最后一帧的时候没有处理直接assert退出了。不过反正也执行结束了。希望接下来的地方没有问题了。
然后是实现closure这是最后的一个东西了,暂时实现生成器了,我准备留到以后了解异步的时候在实现它,我对这个东西的了解实在太少了。
而class的实现我在这里先来讲一下吧。它的实现方式是利用了一个 __build_class__这个是一个內建函数(pycharm会出现红色的下划线,这只是它识别不了而已),这个內建函数是py3以后才有的。

def test():
    class a:
        def ac(self):
            pass

创建一个class的字节码是这样的。

  8           0 LOAD_BUILD_CLASS
              1 LOAD_CONST               1 (<code object a at 0x0031F610, file "D:/PythonProject/VirtualMachine/test.py", line 8>)
              4 LOAD_CONST               2 ('a')
              7 MAKE_FUNCTION            0
             10 LOAD_CONST               2 ('a')
             13 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
             16 STORE_FAST               0 (a)
             19 LOAD_CONST               0 (None)
             22 RETURN_VALUE

首先LOAD_BUILD_CLASS载入一个__build_class__,然后就是两个参数一个是类名,一个是code对象,然后就是调用__build_class__这个了。
class的实现非常简单,就和原先我们所做的差不多,如果你看了如何生成class的字节码你会发现,生成一个class和调用一个function是差不多的,所以相当于在调用一个class的factory方法吧,而生成一个class的第一步也是生成一个function然后把function传给__build_class__这样实现的一个class。现在我们来看看<code object a at 0x0031F610, file "D:/PythonProject/VirtualMachine/test.py", line 8>这个东西好了,看看它到底干了什么事情

def test():
    class a:
        def ac(self):
            pass
dis.dis(test.__code__.co_consts[1])

0 LOAD_NAME 0 (__name__)
3 STORE_NAME 1 (__module__)
6 LOAD_CONST 0 ('test.<locals>.a')
9 STORE_NAME 2 (__qualname__)

9 12 LOAD_CONST 1 (<code object ac at 0x01B9F750, file "D:/PythonProject/VirtualMachine/test.py", line 9>)
15 LOAD_CONST 2 ('test.<locals>.a.ac')
18 MAKE_FUNCTION 0
21 STORE_NAME 3 (ac)
24 LOAD_CONST 3 (None)
27 RETURN_VALUE
除了store_name、load_name以外的指令我们都实现了,不多说了,以及文章里面也说了这两个指令的实现也不多说了,它们就是把名字给储存而已了。

继续闭包的实现。前几步都很正常但是到了最后的 MAKE_CLOSURE 报错由于我不懂 types.FunctionType也没有资料所以只好作罢了,当然可以用类似js的方式去上一个frame里面找的但是目前还是算了吧。 作者的实现也比较难懂,就先不要在这里浪费时间了吧。总之,除了生成器和闭包以外的都以及实现就差不多了,接下来我准备实现以下编译环节的东西了,实现这个以后我会去看cpython源码、做一个自己的编程语言(可以在网页上用的那种)然后回来继续这个文章并且我会重写一遍代码的,现在的代码就先放在我的github上吧,这大概是2个月以后的事情了

${MAKE} -f Makefile clean jvmg ALT_BOOTDIR=/usr/java/jdk1.6.0_32 ARCH_DATA_MODEL=64 LANG=C   

来自为知笔记(Wiz)

1.实现python的虚拟机

 JAVA_HOME  :/usr/java/jdk1.6.0_32

./kfs-5.0/build/classes --- This will contain the Java class files

第二部分

来看看虚拟机为什么叫做虚拟机,当然这是我个人的理解。
首先我们把那一段字节码给反汇编了(把字节码变成可以阅读的方式)。
使用dis模块的dis(disassembly,即反汇编的简写。)

import dis
def acfun():
    a=100

dis.dis(acfun.__code__.co_code)
'''
输出结果是
          0 LOAD_CONST               1 (1)
          3 STORE_FAST               0 (0)
          6 LOAD_CONST               0 (0)
          9 RETURN_VALUE
还有更加简单的方式直接 dis.dis(acfun)
'''

你的python代码会被转换成这样子的一大串一大串的东西(中间还要生成中间代码,大概是ast之类的东西变过来的。)然后交给虚拟机执行它们(这个时候,它们就是目标代码了,我们的目标就是执行这些目标代码,当然啦我过一段时间也会写写关于如何生成中间代码的东西)。
再来,看汇编代码,它没有什么作用(如果你不懂汇编那么应该学学汇编)

    mov ds,ax
    mov si,0
    mov ax,stack
    mov ss,ax
    mov sp,16
    mov dh,8
    mov dl,3
    mov cl,01000010b

    mov ax,0b800h
    mov es,ax
    call show_str

    mov ax,4c00h
    int 21h

你会发现,他们真的太像了(至少形状看起来差不多,对吧。当然汇编代码前面没有数字——它们用来表示偏移量。)
学过c/c++的应该知道一点,它们其实是一种高级的汇编语言,他们最终变成汇编语言。它们最终会变成汇编语言,然后执行在你的机器上(就是目标机器)。

所以说c/c++的“虚拟机”就是你的真实机器。而虚拟机就是把一个程序当成真的机器,让它执行它的“汇编代码”就是字节码。
cpython就是用c语言实现的python虚拟机。它的每一条字节码都代表了一大串的c语言代码。
这也解释了为什么有些编程语言说自己可以跨平台,就是因为它的目标机器是一个自己实现的虚拟机而不是物理机器,不同的物理机器有不同的汇编指令,所以汇编的跨平台能力最弱。


 

./kfs-5.0/build/kfs-{version}.jar --- The jar file containing the Java classes

啊啊啊从今天开始所有的课就上完了,虽然还是要准备考试但是空闲时间是很多的。所以来试试完成这个上面所说的东西。
不过事先得说说,我不是专业学生,我也没学过编译原理,所以说肯定有很多是不对的地方,如果你懂,请告诉我那些地方我不对(甚至根本整篇文章都是错了)。
同时为了阅读方便我不会省略代码,也就是说每一段代码都是完全的,不会因为出现在了前面就后面不写了,所以为了流量着想最好在电脑上看。同时我也希望你可以找我要一下我自己做的一个chrome小插件,它可以实现锚点,让你快速阅读。
用python实现python解释器(虚拟机),用500行不到的python代码。
http://aosabook.org/en/500L/a-python-interpreter-written-in-python.html
中文版的文章在这里
https://linux.cn/article-7753-1.html#4_1568

 

安装:  

第三部分。概念说到了这里。那么,可以开始动手了,请先看看上面链接里面的文章吧。看一半左右就好了,不用看完它,因为后面一次性实现了太多的功能使得难以理解,看我这个文章可以一步一步完成,同时我想应该也会提出更多的问题来(比如python的dictbuildin、以及一些作用域之类的,和python在设计上的一些问题)。

假设你已经成功实现了变量加法了。也看见了真正的python字节码(看这里更多的字节码)我们可以开始试试不要用手工输入指令了。让它自动跑起来实现四则运算。
好了让我们看看现在的代码好了,它现在还不可以实现四则运算,因为它确实了关键的部分。(当然可以用简单的方式实现,但是这样毫无意义。)
我的项目结构

图片 1

image.png

//test.py
from src.VirtualMachine import VirtualMachine
import dis
def test():
    a=10
dis.dis(test)
vm = VirtualMachine()
vm.run_code(test.__code__)

import dis
class VirtualMachine:
    def __init__(self,globals=None,locals=None):
        self.offset=0
        self.frame=None
    def parse_byte_and_args(self):
        code = self.code.co_code[self.offset]
        name = dis.opname[code]
        frame = self.frame
        arg=None
        if code >=dis.HAVE_ARGUMENT:
            offsetOfArg=frame.co_code[self.offset+1]+(frame.co_code[self.offset+2]*2**8)
#解释一下这里,和汇编语言非常类似的,参数由一个字组成,前面的为第8位后面为高8位,它们组成了一个16位的参数,你也可以用位运算实现。
            if code in dis.hasname:
                pass
            elif code in dis.hasconst:
                arg = frame.co_consts[offsetOfArg]
            elif code in dis.haslocal:
                arg = frame.co_varnames[offsetOfArg]
            self.offset+=3
        else:
            self.offset+=1

        return name,arg
    def run_code(self,code,global_names=None, local_names=None):
        self.code=code
        self.frame=code
        while True:
            byte_name,arg=self.parse_byte_and_args()
            print(byte_name,arg)
            if byte_name == "RETURN_VALUE":
                break
            self.dispatch(byte_name, arg)
#上面的代码说的是我在遇到返回之前不断循环它,解析参数。关于如何解析参数的应该看下就会明白的吧。我并没有实现下面的方法。
    def dispatch(self,byte_name, argument):
        pass
    def byte_STORE_FAST(self,name):
        pass
    def byte_LOAD_CONST(self,const):
        pass
    def byte_RETURN_VALUE(self):
        pass

它们的输出结果是这个。

  5           0 LOAD_CONST               1 (10)
              3 STORE_FAST               0 (a)
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE
LOAD_CONST 10
STORE_FAST a
LOAD_CONST None
RETURN_VALUE None

在实现这些指令之前,来看看一个概念吧。作用域。大概大家都知道了吧。但是具体看看python和js和c++的对比。(我现在终于体会到会多种编程语言的好处了。)
有什么不一样的地方呢。

//js1
function acfun(){
    console.log(a);
}
function bili(){
    a=100;
    acfun();
}
bili()
//正常输出

//js2
function acfun(){
    function bili(){
        console.log(a)
    }
    var a=100;
    bili()
}
acfun()
//正常输出

#py1
def acfun():
    print(a)
def bili():
    a=100
    acfun()
bili()
#报错

#py2
def acfun():
    a=100
    def bili():
        nonlocal a
        print(a)
    bili()
acfun()
#正常输出

#include <iostream>
using namespace std;
void say(){
    cout<<a;
}
int main()
{
    a=100;
    say();
}
//报错

这里就不演示c++的lambda啦。
很明显,python的很多地方都更加接近c++,没有闭包的情况下它们都是词法作用域(静态作用域)不会去外面找除非是在global下(具体查看pyhon的LEGB )。而js是动态作用域的(请看关于JS的栈的内容。)。而同时请看看JS和python的闭包吧。事实上js的闭包是不完整的如果你去掉nolocal(只有py3以后才有的)并且试图修改它就会报错哟,当然python在闭包的时候也不会去访问上一层调用栈里面的东西的,而是基于legb原则去包住它的函数里面找。
而同时关于python的lambda表达式,会出现这样的情况。

funcs = [(lambda n: i * n) for i in range(5)]
print(funcs[0](2))
print(funcs[1](2))

输出都是一样的因为生成了4个lambda表达式可是里面的每一个i的只是一个引用而已。同时python还有这样的特性就是对于 Int形的变量来说如果值是一样的情况下那么他们就是同一个。

a=1
def acfun():
    b=1
    print(id(b))
print(id(a))
acfun()
'''
1348901312
1348901312
'''

还有关于python的更多特性暂时不讲了,我会在其他的文章里面说的(在我看完《两周自制脚本语言》之后),继续我们的实现,抱歉我也不知道这一段是不是废话来着。
你也看过那篇文章了,那么我就直接继续咯。
我们必须有你个东西来表示我们现在的作用域了,于是轮到Frame 类出场了。目前为止,我们只需要有一个Frame就好了来看这个东西。

import builtins
def make_frame(self, code, callargs={}, f_globals=None, f_locals=None):
       f_globals = f_locals = {
                '__builtins__': builtins,
                '__name__': '__main__',
                '__doc__': None,
                '__package__': None,
            }
        frame = Frame(code, f_globals, f_locals, self.frame)
        return frame

我来逐个解释一下里面的东西好了 。从简单的开始好了。__doc__就是一个说明

def test():
    '''
    什么也没有
    '''
    a=10

print(test.__doc__)
#输出
'''

    什么也没有


'''

__name__
就是这个文件的名字如果是入口的话就叫做__main__如果是被import的那就是文件的名字了。我们这个栈帧是入口所以是__main__
__package__
如果是被引入的包就是包的名字了。如果不是就是个None。
最后一个,__bulitin__这个是望文生义就是內建的函数或者对象,当然它本身是一个字典。比如print()就是一个內建的函数了。如果你不怕麻烦你可以这样来用print(),下面的代码完全行得通。

globals()['__builtins__'].__dict__["print"]("我很无聊要这样输出")

好了,说说在__main__里面的和不在的有什么不同吧。
在__main__里面

import builtins
print(id(builtins))
print(id(globals()['__builtins__']))
'''
16475504
16475504
'''

被引入的文件中是这样的

import builtins
print(id(builtins))
print(id(globals()['__builtins__']))
'''
16475504
16479776
'''

以上就是我在被引入的文件中import 了一个 builtins的理由了。我们只有一个帧。好了现在我实现到了for 了,这之间的过程没有什么好说的了。来看看我们的Function类 和 Method 类吧,在实现它们之后我们来实现闭包和生成器。


4写上hotspotmake目录下makefile的路径

# KFS Machine configuration file

第一部分

先来说说关于python的字节码好了。
.py .pyc .pyo
他们分别是以下的简写
python
pythoncode
pythonoptimized
这些都是python文件的拓展名,所以他们的意思都很好理解了。
第二种.pyc就是我要说的东西。Python的字节码。
怎么生成字节码呢。百度上都有的。

好了,事实上生成之后也打不开(不知道UltraEdit可不可以)。
不过我们也不需要打开它。

def acfun():
    a=100
print(acfun.__code__.co_code)
print(acfun)
输出结果
'''
 b'dx01x00}x00x00dx00x00S'
这个就是这个函数的字节码(opcodes,我们就叫它字节码)
<function acfun at 0x02214780>
这个大概是内存地址,不知道是虚拟机里面的内存地址还是物理机的内存地址。它就是你的类里面__repr__所定义的返回值。
'''

获取一个函数字节码就是这样了


9点了调试后会是漫长的等待,知道有一刻你发现断点在main函数中停留了,那就说明是成功了,最后运行完毕后,可以看到控制台的结果如下:

cd build

你能学到的东西

3找到你openjdk下关于hotspot的源码文件夹,并填好,以及选择定制

 

2.python的一些特性

以这两篇文章为基础来实现一个python的虚拟机,python的虚拟机(Virtual Machine)被称作解释器(Interpreter),它的功能类似于JAVA的JVM(JAVA Virtual Machine),我也不知道为什么python的虚拟机被称作解释器(两周自制脚本语言里面说解释器是直接执行抽象语法树的,但是python是会生成字节码的,可能是python解释器内置了一个编译器来着。),总之这篇文章里面都叫虚拟机了而不是解释器。
还要注意一点。无论python还是java的还是js的虚拟机都和Vmware不是一回事,Vmware这个属于硬件虚拟机而python它们的虚拟机不是它们属于程序虚拟机。


虚拟机,众所周知,是一个让class文件运行的平台,那么这次调试,你可以先理解为当class文件运行,我们将断点停留在虚拟机。

tar zxvf kfs-0.5.tar.gz

 

软件库安装到: ./kfs-0.5/build/lib

图片 2

使用yum search xfs,可以看到三个相关的XFS filesystem

  你弄了半天都不知道为什么。当然如果你是大神,你可以直接使用gdb来调试。

2.下载源代码:

这篇文章是调试虚拟机的,其实网上也能找到一些文章,但是每个人的环境不一样,可能有的很顺利,有的就不一样了

#启动服务

图片 3

测试服务:

图片 4

单机的,bin下面的就可以使用了

图片 5

 

2在netBeans中新建基于源码c/c++项目。

#yum install xfsprogs xfsprogs-devel xfsdump

我的是这样的,你们可能需要对照的改一下。

可执行文件安装到:  ./kfs-0.5/build/bin

6一直点下一步,在ide中会出现hotspot项目。

192.168.3.79

 

kfsping -c -s <chunkserver host> -p <chunkserver port>

3这篇文章,怎么说呢。是踩了很多坑得出来了,也是在自己快要崩溃的时候得出来了的。

编译安装java支持:

 

# python kfssetup.py -f machines.cfg -m machines.txt -b ../build -w ../webui

CLASSPATH:.:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar

见hypertable安装(cmake 2.8)

图片 6

如:

图片 7

cmake (preferably, version 2.4.6 or higher)

/usr/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg/gamma -XX:StopInterpreterAt=1 -version /usr/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg/Queens

 

其实还有另一点。你电脑上如果jdk是1.8的,你的netBeanIde运行不起来,所以低版本就好,我的是1.6.0_32。

见hypertable安装(log4cpp-1.1)

LD_LIBRARY_PATH: /usr/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg

xfs devel RPMs on Linux

 

集群的部署

但是踩了坑比较多,你才会横向发现有些别人其实是没讲到位的,也就是一笔带过的。

python kfslaunch.py -f machines.cfg -m machines.txt -S

环境变量如下:

cd kfs-0.5

 

mkdir build  #建立临时目录

上一篇文章我写了关于编译openjdk的文章,这篇实际上是接着上一篇来的。

这里,我修改了/etc/profile,加入

图片 8

 

 最后保存上面的更改。

 

 

kfsping -m -s <metaserver host> -p <metaserver port>

 运行命令的意思是,用什么去运行什么,第一个什么就是gamma,这个gamma就是虚拟机程序,第二个什么就是指的你要在虚拟机上运行的class文件。当然class文件的运行

192.168.3.80

图片 9

 

openjdk编译后会成为一个可用的jdk,jdk中就包含了虚拟机。

 

8在下图的文件层次结构下找到java.c这个就是虚拟机的入口了,你可以先打上断点。然后右击项目选择调试

 

中间的alt_bootdir实际上我上篇文章编译openjdk的那个oracle jdk1.6。至于为什么要用这个版本大家可以去查一下,

参考

来,我们一步步顺着好理解的逻辑来解决这个问题。

安装效果:

  这里要提示两点,第一点,下载ide的时候要下载c++版本的。第二点ide最好下载7.0.1版本的,因为有些版本无法跳入到断点,这算一个坑

#安装

1既然是要调试虚拟机,那我们最好是不是要一个ide来调试呢,由于虚拟机是c编写的,所以我们下载一个netBeans Ide。

见hypertable的安装(boost 1.44)

谢谢各位,如果看完了,希望能关注一波,写这个东西很费时间的,文章不是重点,交个朋友才是重中之重!

 

实际上是为了调试虚拟机,这点要清楚,你class都没有运行,怎么调试你的虚拟机呢。我的命令如下:

3.编译

10现在你就可以将你的程序替换上面的Queens类文件了,当你的程序运行的时候你就可以进一步窥探虚拟机怎么运行的了。

gcc version 4.1 (or higher)

运行的class文件地址。你大可写成绝对路径,就不用在后续的classpath地址上加这个长路径了。

export CLASSPATH=$CLASSPATH:/home/lijian/download/hypertable/kfs-0.5/build/kfs-0.5.jar

连续踩了差不多10来个小时的坑,还好是出来了。

 

7右键点击项目,选择属性,再点运行,又出来三个需要配置的地方。

python kfssetup.py -f machines.cfg -m machines.txt -b ../build/bin -U

 

cd kfs-0.5/scripts

注意中间的空格,gamma是hotspot源代码编译后生成的,要生成后才有,但是你要找到你gamma的路径,后面的两个是gamma的参数,最后的一个长路径是你要

#gmake -j16 (有16核,使用多个线程,)

 

 

图片 10

cmake  -D CMAKE_BUILD_TYPE=RelWithDebInfo ../  #加入debug信息

有了这个初步的感性认识你可能好理解得多了。

metaserver节点下必须设置 clusterkey,值可以随便设置,可以理解为一个集群的标识。

运行目录我填的是/usr/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg。

 

 

#gmake install

5下一步要填写一些生成命令,我的是如下:

log4cpp (preferably, version 1.0)

那就说明能调试成功了。

#卸载

[metaserver]
node: de79
clusterkey: kfs-test-cluster
rundir: /mnt/kfs/meta
baseport: 20000
loglevel: INFO
numservers: 2
[chunkserver_defaults]
rundir: /mnt/kfs/chunk
chunkDir: /mnt/kfs/chunk/bin/kfschunk
baseport: 30000
space: 3400 G
loglevel: INFO

来源: <>

cd  kfs-0.5

编译安装c++支持:

192.168.3.81

现在是0.5版本

编辑配置文件kfs-0.5/scripts/下的machines.cfg

cd kfs-0.5/build/bin/tools

#停止服务

python kfslaunch.py -f machines.cfg -m machines.txt -s

export CLASSPATH=${CLASSPATH}:~/code/kfs/build/kfs-[version].jar

 

建立一个chunk结点的文件 ,这里是machines.txt:

安装效果:

安装:

ant jar

 

 

 

自带

加入到环境变量CLASSPATH :

kfs编译安装:  

安装

wget

python 的支持用不到,需要的话可以从参考中学习使用

官网:

官网:

Boost (preferably, version 1.34 or higher)

1.安装依赖包:

本文由9159.com发布于前端,转载请注明出处:3这篇文章,build对class文件进行更新

关键词: