2011-03-14 50 views
2

我正在开发一个使用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下载)。

回答

0

我会在模块加载时创建JVM实例(_init(void)函数或apache自称的东西)并在_fini上销毁它。

如果我没有记错的话,可以通过AttachCurrentThread将多个线程附加到JVM。但是你需要确保返回的指针是,该线程只使用。我会建议使用线程本地存储。您可以在线程创建时将指针初始化为0,并在新线程对函数的第一次调用中执行附加操作。

+0

感谢您的回答!你的建议听起来似乎合理。当Apache启动时,它调用PHP_MINIT_FUNTION函数,在那里我想我可以创建JVM。在Apache关闭时,调用PHP_MSHUTDOWN_FUNCTION。我将Zend对象存储用于所有与对象有关的变量,这里也是存储在结构中的JVM句柄(除了Env句柄)。它应该是线程安全的。所以如果我理解正确的话,每当我想在扩展的类方法中使用JVM时,我只需附加到该JVM,调用Java的东西并在方法内分离?不需要创建额外的线程,对吗? – 2011-03-15 06:17:18

+0

Apache使用线程或分叉进程。因此,函数总是不可能从同一个线程中调用。附加和分离可能相当昂贵,所以我会避免每个函数调用。 – 2011-03-15 15:28:34

+0

这个建议似乎工作到目前为止。我在'php_minit'中创建JVM,并在'php_mshutdown'中销毁它。 JVM对象被定义为全局的(我不认为,我需要关心模块启动/关闭时的线程安全性)。模块类的构造函数然后附加到JVM并且析构函数再次分离。 JVM JNI相关对象(JNIEnv等)存储在一个结构中,并由zend对象存储安全地处理。好像我可以调用几个php类的方法(我的JNI Java fct的包装)没有任何问题。它还没有完成,但至少我没有得到Apache的segfaults到现在。 – 2011-03-18 09:53:59