我正在开发一个使用JNI调用Java类的PHP5扩展。这样做的扩展和核心功能已经准备就绪,并且在单线程(每个进程)环境中按预期工作。因此,在命令行(php-cli)上使用php脚本中的扩展类功能没有任何问题。 现在使用该PHP扩展作为apache模块,在通过JNI创建JVM时遇到问题。使用该模块的第一个调用工作正常,第二个调用(在第一个调用运行时以及在第一个调用完成后),JVM不会创建并返回一个小于0的代码(JNI_ERR
)。正如我发现的那样,每个进程一次只能有一个JVM是一个限制。我测试并运行我的模块运行Apache只有一名工人(使用-X
开关)。使用JVM构建PHP扩展,需要实现思路
我在现阶段模块的设计是这样的:
- 扩展构造函数创建JVM,使一些初始化。
- 然后我有几个方法来调用和使用java类(如传递参数等),并执行一些java类的方法。
- 我的扩展析构函数释放了所有使用的资源,并销毁了以前在构造函数中创建的JVM。
那么,首先我想说,一切都会解决,但事实并非如此。不像现在这样实施。所以我有点卡在想法如何解决这个问题。
我的问题是: 我应该在哪里创建JVM,以及如何在线程环境(PHP作为Apache模块)中正确调用它(JNI Env)。我想我必须在模块初始化时使用JNI_CreateJavaVM
在某处创建JVM,然后使用AttachCurrentThread
/DetachCurrentThread
?!如果另一个线程试图连接到JVM线程,但是已经有另一个线程连接到它,会发生什么?我必须在这里做一些锁定机制吗?或者在扩展类方法中创建JVM,在该方法内调用所有与Java相关的方法,并在同一扩展类方法的末尾销毁JVM?我根本不喜欢这个想法。尽管我认为它不能解决每个进程有多个JVM的问题。
我希望事情不要太复杂。
所以,基本上,我的问题是在哪里实现JVM创建/销毁调用以及如何访问JNI环境来调用Java类方法。最后但并非最不重要的是,无论如何可能我试图完成什么?
一些开发细节:我的平台是Linux/Ubuntu Lucid 10.04 LTS,使用Apache2和PHP 5.2.16(从源代码编译)。模块和核心功能是用C开发的,Java JRE是6.0.22(Oracle下载)。
感谢您的回答!你的建议听起来似乎合理。当Apache启动时,它调用PHP_MINIT_FUNTION函数,在那里我想我可以创建JVM。在Apache关闭时,调用PHP_MSHUTDOWN_FUNCTION。我将Zend对象存储用于所有与对象有关的变量,这里也是存储在结构中的JVM句柄(除了Env句柄)。它应该是线程安全的。所以如果我理解正确的话,每当我想在扩展的类方法中使用JVM时,我只需附加到该JVM,调用Java的东西并在方法内分离?不需要创建额外的线程,对吗? – 2011-03-15 06:17:18
Apache使用线程或分叉进程。因此,函数总是不可能从同一个线程中调用。附加和分离可能相当昂贵,所以我会避免每个函数调用。 – 2011-03-15 15:28:34
这个建议似乎工作到目前为止。我在'php_minit'中创建JVM,并在'php_mshutdown'中销毁它。 JVM对象被定义为全局的(我不认为,我需要关心模块启动/关闭时的线程安全性)。模块类的构造函数然后附加到JVM并且析构函数再次分离。 JVM JNI相关对象(JNIEnv等)存储在一个结构中,并由zend对象存储安全地处理。好像我可以调用几个php类的方法(我的JNI Java fct的包装)没有任何问题。它还没有完成,但至少我没有得到Apache的segfaults到现在。 – 2011-03-18 09:53:59