Java Native Interface(一)

最近在整理学习笔记的时候发现了去年年中记录的 JNI 学习笔记,由于存放在了为知笔记中,而如今为知笔记已经不再免费,于是想到了将其重新整理一遍,一来可以巩固所学,二来能将其迁移到本地

有时候,使用 native code(c/c++)来克服 Java 中的内存管理和性能的局限性是很有必要的。Java 支持 native codes,被称作 Java Native Interface(JNI)。

JNI 非常难,毕竟它牵涉到了两种编程语言。假设聪明的你对 Java 和 C/C++以及 GCC 编译器已经有所了解。那么下面就一起来一步步学习 JNI 吧。

Step1: 创建一个名字为 JNITest.java 的文件

public class JNITest {
    static {
        System.load("/home/ubuntu/workspace/java/jni/mynativelib.so");
    }

    //申明一个无参的native方法,而且返回空
    public native void greet();

    //测试
    public static void main(String[] args) {
        JNITest test = new JNITest();
        test.greet();
    }
}

首先用静态代码块加载本地动态链接库"mynativelib.so"。对于静态代码块,我相信写过 Java 的你应该非常清楚,它只会在类被加载的时候执行一次。这个动态链接库会被添加到 Java 的 library path(保存在 Java 系统变量 java.library.path)中,如果加载失败,就会抛出UnsatisfiedLinkError异常。也可以使用 JVM 启动参数来加载该动态链接库到 Java 的 library path 当中:

基于c语言的编程语言开发

当今世道,各种高级语言百花齐放。然而会有人发出这样的疑问–计算机真的能够识别这么多语言吗?稍微有点常识的人都知道,这显然是不可能滴!在计算机的世界里,他们能够直接识别的只有机器语言。然而,由于机器语言对人类不够友好,所以人们才发明了汇编,c,Java…许许多多的人类易读的编程语言,所以我个人对编程语言的理解一直是其实他们就是机器语言的语法糖,而编程语言的创造过程,就是定义一种合理的,没有二义性的语法规则,然后就是通过直接或间接的方式实现该语法到机器语言的转换过程。既然是这样的话,那么我们就很容易想到,计算机语言是一个自我完善的过程:首先我们定了一种非常简单的 x1(这里只是用来举例说明,有没有 x 语言有待考证)语言,然后用机器语言实现了这个非常简单的 x1 语言的编译器,创造了 x1 语言,实现了非常简单的新特性,然后我们再用 x1 语言(相对于机器语言较高级)实现了另一些新的特性的 x2 语言的编译器,创造了 x2 语言,…,如此下去,人们创造了汇编语言,从而创造了 c 语言,接着创造了世界上最好的语言 PHP(不知道是不是真的,反正大家都习惯这么说)。 在各种高级语言越来越强大的今天,我们可能很难再会去接触最原始的东西,高度封装确实提高了生产力,降低了学习成本,但是也使得现代程序员将太多精力花在了各种说明书上,而不清楚其本质。 毕业一年多,工作了一年多,对于计算机编程有了自己的看法,不再像在大学的时候认识的那样肤浅,反而觉得大学中学习的知识才是真正的干货,不禁感叹曾经浪费掉了大好光阴。好在陶渊明有词云:“悟已往之不谏,知来者之可追”。 闲暇之余,扒开 PHP(这里之所以是 PHP 并不因为他是世界上最好的语言,只是因为我目前从事的是 PHP 开发的工作而已)源码,了解了其内部构造和实现原理,百看不一练。今天就初步学习 yacc/lex 了,记录在我的博客中,以便以后翻阅巩固。

一般来说编程语言的解释执行过程如下:

ONE. 词法分析 将源代码拆分成若干 Token 的过程

TWO.语法分析 将 Token 构建成 Syntax Tree 的过程

THREE.生成执行码 生成可执行文件

下面是 wikipedia 中对 yacc 的描述

Yacc is a computer program for the Unix operating system. It is a Look Ahead Left-to-Right (LALR) parser generator, generating a parser, the part of a compiler that tries to make syntactic sense of the source code, specifically a LALR parser, based on an analytic grammar written in a notation similar to Backus–Naur Form (BNF). Yacc itself used to be available as the default parser generator on most Unix systems, though it has since been supplanted as the default by more recent, largely compatible, programs.

PHP扩展开发之call_user_func原理和回调函数的实现

很多时候,我们需要通过函数名来调用函数,并传递参数,或者把匿名函数作为函数的参数传递,实现回调。当我们在遇到这样的需求的时候,用 php 代码实现起来肯定是非常容易和简单的。但是,当我们在用 c 语言编写 php 扩展的时候,如何来实现这样的功能呢?下面就一起来深入了解 php 内核,看看如何实现。

在 Zend 引擎中,给我们提供了zend_call_function,call_user_function以及call_user_function_ex函数来帮助我们实现函数调用。在zend_API.h文件中,我们可以看到如下函数原型的声明:

ZEND_API int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TSRMLS_DC);
ZEND_API int call_user_function(HashTable *function_table, zval **object_pp, zval *function_name, zval *retval_ptr, zend_uint param_count, zval *params[] TSRMLS_DC);
ZEND_API int call_user_function_ex(HashTable *function_table, zval **object_pp, zval *function_name, zval **retval_ptr_ptr, zend_uint param_count, zval **params[], int no_separation, HashTable *symbol_table TSRMLS_DC);

从函数的参数上来看,显然zend_call_function需要的参数很少,而其他两个都需要一堆参数,所以,我们可能会想,达到相同的效果为什么参数上有如此大的区别,于是带着这个疑问我们来解刨zend_fcall_info结构体。同样在zend_API.h中会看到如下结构体的定义: