JNI を使って Solaris のカーネルステータス(kstat)を取得する方法

(

)

by

in

JNI を使って Java から Solaris のカーネルステータス(kstat)から値を取得する方法
 参考 Web:
http://jimfl.tensegrity.net/jni.html
 Solaris でシステム統計を取得する方法に関しては、以下を参照。
http://www.idiom.com/~gford/admin/howto/perf.html
 C 言語で kstat にアクセスする方法に関しては、以下を参照。
 http://developer.sun.com/solaris/articles/kstatc.html
kstat -m cpu_stat の出力例は、以下の通り


$ kstat -m cpu_stat
module: cpu_stat instance: 0
name: cpu_stat0 class: misc

idle 1363651

iowait 0
kernel 4472

swap 0
swapin 0
swapout 0

user 5537
wait 10511
wait_io 10511


CPU の load を取得する。
1. Interface class である sysInfo.java を作成


public class sysInfo {
public native int init();
public native void destroy();
public native double[] getSysLoad();
public native double[] getAvgLoad();
static {
System.loadLibrary(“sysinfo”);
}
}

2. 1.で作成した sysInfo.java をコンパイル


$ javac sysInfo.java

3. javah コマンドで JNI 用ライブラリの為の C/C++ ヘッダファイルを生成


$ javah -jni sysInfo

作成されたヘッダファイル sysInfo.h の中身


/* DO NOT EDIT THIS FILE – it is machine generated */
#include
/* Header for class sysInfo */
#ifndef _Included_sysInfo
#define _Included_sysInfo
#ifdef __cplusplus
extern “C” {
#endif
/*
* Class: sysInfo
* Method: init
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_sysInfo_init
(JNIEnv *, jobject);
/*
* Class: sysInfo
* Method: destroy
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sysInfo_destroy
(JNIEnv *, jobject);
/*
* Class: sysInfo
* Method: getSysLoad
* Signature: ()[D
*/
JNIEXPORT jdoubleArray JNICALL Java_sysInfo_getSysLoad
(JNIEnv *, jobject);
/*
* Class: sysInfo
* Method: getAvgLoad
* Signature: ()[D
*/
JNIEXPORT jdoubleArray JNICALL Java_sysInfo_getAvgLoad
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

4. 3. で生成したヘッダファイルにある C 関数を実装する。
以下は、C で書かれた実装 sysInfoImp.c の例。


#include
#include
#include
#include
#include
#include
#include “sysInfo.h”
#ifndef FSCALE
#define FSHIFT 8
#define FSCALE (1< 0){
return(ncpus);
} else {
return(0);
}
}
JNIEXPORT void JNICALL Java_sysInfo_destroy (JNIEnv *env, jobject obj){
destroy();
}
JNIEXPORT jdoubleArray JNICALL Java_sysInfo_getSysLoad (JNIEnv *env, jobject obj
){
jdoubleArray sys_load;
jdouble *sys_load_ptr;
int i;
sys_load = (*env)->NewDoubleArray(env, CPUSTATS);
if (!sys_load) {
perror(“Error at NewDoubleArray”);
return(0);
}
sys_load_ptr = (*env)->GetDoubleArrayElements(env, sys_load, 0);
(void) getSysLoad((double *) sys_load_ptr);
(*env)->ReleaseDoubleArrayElements(env, sys_load, sys_load_ptr, 0);
return(sys_load);
}
JNIEXPORT jdoubleArray JNICALL Java_sysInfo_getAvgLoad (JNIEnv *env, jobject obj
) {
jdoubleArray load_avg;
jdouble *load_avg_ptr;
load_avg = (*env)->NewDoubleArray(env, LOAD_AVGS);
if (!load_avg) {
perror(“NewDoubleArray”);
return(0);
}
load_avg_ptr = (*env)->GetDoubleArrayElements(env, load_avg, 0);
(void) getAvgLoad((double *) load_avg_ptr);
(*env)->ReleaseDoubleArrayElements(env, load_avg, load_avg_ptr, 0);
return(load_avg);
}
/* init() — Initialize to fetch kstat and return number of cpus */
int init(){
int i;
kstat_named_t *kn;
int ncpus;
for(i = 0; i value.ui32; }
//printf(“CPU:%dn”,ncpus);
return ncpus;
}
/* getSysLoad() — get Total cpu’s load */
void getSysLoad(double *sys_stat){
cpu_stat_t cs;
kid_t nkcid;
int i;
int cs_stat[CPUSTATS];
nkcid = kstat_chain_update(kc);
kcid = nkcid;
for(i = 0; i kc_chain; ks; ks = ks->ks_next) {
if (strncmp(ks->ks_name, “cpu_stat”, 8) == 0) {
if((nkcid = kstat_read(kc, ks, &cs)) == -1){
perror(“kstat_read”);
break;
}
for(i = 0; i < CPU_WAIT; i++){ // loop 0(idle), 1(user), 2(kernel)
cs_now[i] += cs.cpu_sysinfo.cpu[i];
}
cs_now[CPUSTAT_IOWAIT] += (long)cs.cpu_sysinfo.wait[W_IO] +
(long)cs.cpu_sysinfo.wait[W_PIO];
cs_now[CPUSTAT_SWAP] += (long)cs.cpu_sysinfo.wait[W_SWAP] ;
}
}
(void) percentages (CPUSTATS, cs_stat, cs_now, cs_old, cs_diff);
for(i = 0; i value.ul; }
kn = kstat_data_lookup(ks, “avenrun_5min”);
if (kn) { avenrun[1] = kn->value.ul; }
else { avenrun[1] = 99; }
kn = kstat_data_lookup(ks, “avenrun_15min”);
if (kn) { avenrun[2] = kn->value.ul; }
else { avenrun[2] = 99; }
for (i=0; i<LOAD_AVGS; i++) {
load_avg[i] = loadcvt(avenrun[i]);
}
}
/* destroy() — close kstat */
void destroy(){
(void) kstat_close(kc);
}
/*
* percentages(cnt, out, new, old, diffs) – calculate percentage change
* between array "old" and "new", putting the percentages i "out".
* "cnt" is size of each array and "diffs" is used for scratch space.
* The array "old" is updated on each call.
* The routine assumes modulo arithmetic. This function is especially
* useful on BSD mchines for calculating cpu state percentages.
*/
long percentages(cnt, out, new, old, diffs)
int cnt;
int *out;
register long *new;
register long *old;
long *diffs;
{
register int i;
register long change;
register long total_change;
register long *dp;
long half_total;
/* initialization */
total_change = 0;
dp = diffs;
/* calculate changes for each state and the overall change */
for (i = 0; i < cnt; i++)
{
if ((change = *new – *old) < 0)
{
/* this only happens when the counter wraps */
change = (int)
((unsigned long)*new-(unsigned long)*old);
}
total_change += (*dp++ = change);
*old++ = *new++;
}
/* avoid divide by zero potential */
if (total_change == 0)
{
total_change = 1;
}
/* calculate percentages based on overall change, rounding up */
half_total = total_change / 2l;
for (i = 0; i < cnt; i++)
{
*out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
}
/* return the total in case the caller wants to use it */
return(total_change);
}

* kstat の帰り値は 64bit なので、jdouble を使用している。
(jlong (64bit int)でも良いかも)
5. 共有ライブラリ(libsysinfo.so)を作成。以下は gcc での例。(gcc がインストールされていない場合は sunsite 等から gcc をダウンロードして pkgadd する)


gcc -G -I/usr/j2se/include -I/usr/j2se/include/solaris -lkstat -o libsysinfo.so sysInfoImp.c

* 今回は、libkstat.so も使用するので、ライブラリに含めている。
6. 呼び側のプログラム(TestJNI.java)を作成


import java.io.*;
import java.text.NumberFormat;
class TestJNI {
public static void main(String[] args) {
new sysStat().start();
//sysStat sys = new sysStat();
//sys.printStat();
}
}
class sysStat extends Thread {
final int CPUSTATS = 5;
final String[] cs_name ={“IDLE”,”USER”,”KERNEL”,”IOWAIT”,”SWAP”};
final String[] la_name ={“1min”,”5min”,”15min”};
NumberFormat fmt;
sysInfo sysinfo;
public void run(){
for(int i = 0; i < 5; i++){
printStat();
try{
sleep(5000);
} catch(InterruptedException ex){
}
}
destroys();
}
sysStat(){
fmt = NumberFormat.getInstance();
fmt.setMaximumFractionDigits(2);
fmt.setMinimumFractionDigits(1);
sysinfo = new sysInfo();
sysinfo.init();
}
void printStat(){
double[] stat = sysinfo.getSysLoad();
System.out.println(" ");
for(int i = 0; i < CPUSTATS; i++){
System.out.print(" " + cs_name[i] + "[" + fmt.format(stat[i]) + "%]");
}
double[] load = sysinfo.getAvgLoad();
System.out.println(" ");
for(int i = 0; i < 3; i++){
System.out.print(" " + la_name[i] + "[" + fmt.format(load[i]) + "%]");
}
}
void destroys(){
sysinfo.destroy();
}
}

7. 呼び側のプログラムをコンパイル


$ javac TestJNI.java

8. LD_LIBRARY_PATH を libsysinfo.so があるディレクトリに通しておく。


$ export LD_LIBRARY_PATH=.

9. 実行


$ java TestJNI