原文: http://nullwy.me/2018/01/java...
如果觉得我的文章对你有用,请随意赞赏
遇到的问题
前段时间开发的时候,遇到一个问题,就是如何用 Java 实现 chdir
?网上搜索一番,发现了 JNR-POSIX
项目 [stackoverflow ]。俗话说,好记性不如烂笔头。现在将涉及到的相关知识点总结成笔记。
其实针对 Java 实现 chdir
问题,官方 20 多年前就存在对应的 bug,即 JDK-4045688 'Add chdir or equivalent notion of changing working directory'。这个 bug 在 1997.04 创建,目前的状态是 Won't Fix
(不予解决),理由大致是,若实现与操作系统一样的进程级别的 chdir
,将影响 JVM 上的全部线程,这样引入了可变(mutable)的全局状态,这与 Java 的安全性优先原则冲突,现在添加全局可变的进程状态,已经太迟了,对不变性(immutability)的支持才是 Java 要实现的特性。
chdir
是平台相关的操作系统接口,POSIX 下对应的 API 为 int chdir(const char *path);
,而 Windows 下对应的 API 为 BOOL WINAPI SetCurrentDirectory(_In_ LPCTSTR lpPathName);
,另外 Windows 下也可以使用 MSVCRT 中 API 的 int _chdir(const char *dirname);
(MSVCRT 下内部实现其实就是调用 SetCurrentDirectory
[reactos ] )。
Java 设计理念是跨平台,"write once, run anywhere"。很平台相关的 API,虽然各个平台都有自己的类似的实现,但存在会差异。除了多数常见功能,Java 并没有对全部操作系统接口提供完整支持,比如很多 POSIX API。除了 chdir
,另外一个典型的例子是,在 Java 9 以前 JDK 获取进程 id 一直没有简洁的方法 [stackoverflow ],最新发布的 Java 9 中的 JEP 102(Process API Updates)才增强了进程 API。获取进程 id 可以使用以下方式 [javadoc ]:
long pid = ProcessHandle.current().pid();
相比其他语言,Pyhon 和 Ruby,对操作系统相关的接口都有更多的原生支持。Pyhon 和 Ruby 实现的相关 API 基本上都带有 POSIX 风格。比如上文提到,chdir
和 getpid
,在 Pyhon 和 Ruby 下对应的 API 为:Pyhon 的 os 模块 os.chdir(path) 和 os.getpid();Ruby 的 Dir 类的 [Dir.chdir( [ string] )](https://ruby-doc.org/core-2.2... 类方法和 Process 类的 Process.pid 类属性。Python 解释器的 chdir
对应源码为 posixmodule.c#L2611,Ruby 解释器的 chdir
对应源码为 dir.c#L848 和 win32.c#L6741。
JNI 实现 getpid
Java 下要想实现本地方法调用,需要通过 JNI。关于 JNI 的介绍,可以参阅“Java核心技术,卷II:高级特性,第9版2013”的“第12章 本地方法”,或者读当年 Sun 公司 JNI 设计者 Sheng Liang(梁胜)写的“Java Native Interface: Programmer's Guide and Specification”。本文只给出实现 getpid
的一个简单示例。
首先使用 Maven 创建一个简单的脚手架:
mvn archetype:generate \
-DgroupId=com.test \
-DartifactId=jni-jnr \
-DpackageName=com.test \
-DinteractiveMode=false
在 com.test
包下添加 GetPidJni
类:
package com.test;
public class GetPidJni {
public static native long getpid();
static {
System.loadLibrary("getpidjni");
}