signal 핸들링은 (제 생각엔) Java 에선 뜨거운 감자 같습니다. 왜냐면, signal
핸들링은 OS 마다 다른데, 어떤 OS에서든 동작해야 하는 Java 입장에선 일반화된
Java API를 제공하기엔 바람직하지 않은 듯 합니다.
예를 들어 kill 명령어는 Unix 에서는 일반적이지만, Windows 에선 (제가 알기로는)
kill 명령어가 없듯이 (즉, signal 를 해당 process 에게 줄수 있는 방법이 없듯이)
java 에서 signal 핸들링은 결국 OS 종속적인 JNI 를 이용해 필요할 때 알아서
사용토록하고, Java 는 관여치 않는 듯 합니다.
C 에서 signal 핸들링을 해 보셨고, 또, Java 의 JNI 를 해 보셨다면, 저처럼
어렵지 않게 다음과 같은 테스트를 해 볼 수 있을 겁니다.
첫번째 예제는 실행된 Java 프로그램에서 가능한 모든 종류의 signal 을 잡아보는
예제입니다. 기본개념은 JNI C 메소드를 호출해 signal 을 등록하고, C native 코드에서
signal 이 오면, Java 의 특정 메소드를 JNI callback 을 이용해 호출시켜 주는
구조입니다.
javaservice:/home/guest/source/signal$ ls -alF
total 12
drwxr-xr-x 3 java java 4096 Jan 1 23:21 ./
drwxrwxrwx 6 guest users 4096 Jan 1 23:21 ../
drwxr-xr-x 3 java java 4096 Jan 1 23:20 org/
javaservice:/home/guest/source/signal$ ls -alF org/jsn/signal/
total 12
drwxr-xr-x 2 java java 4096 Jan 1 23:36 ./
drwxr-xr-x 3 java java 4096 Jan 1 23:21 ../
-rw-r--r-- 1 java java 651 Jan 1 23:36 SignalTest.java
javaservice:/home/guest/source/signal# cat org/jsn/signal/SignalTest.java
package org.jsn.signal;
public class SignalTest {
public SignalTest() {
for(int i=1;i<31;i++)
signal(i, "handle");
System.out.println("signal listening");
}
public void do_something(){
// infinite loop
for(int i=0; true ; i++) {
System.out.println("waiting..." + i);
try{Thread.sleep(1000 * 10);}catch(Exception e){}
}
}
public native synchronized void signal(int signum, String handles);
/* This callback method will be called by native code*/
public void handle(int signum) {
System.out.println("I have got signal : " + signum);
if ( signum == 3 || signum == 9 || signum == 15 ) {
System.out.println("gracefully exited, really?");
System.exit(0);// NOTE: this does NOT work;
}
}
static {
try{
System.loadLibrary("signalhandle");
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args){
SignalTest test = new SignalTest();
test.do_something();
}
}
javaservice:/home/guest/source/signal$
javaservice:/home/guest/source/signal$ javac org/jsn/signal/SignalTest.java
javaservice:/home/guest/source/signal$ ls -alF org/jsn/signal/
total 16
drwxr-xr-x 2 java java 4096 Jan 1 23:37 ./
drwxr-xr-x 3 java java 4096 Jan 1 23:21 ../
-rw-r--r-- 1 java java 1088 Jan 1 23:37 SignalTest.class
-rw-r--r-- 1 java java 651 Jan 1 23:36 SignalTest.java
javaservice:/home/guest/source/signal$ javah -jni org.jsn.signal.SignalTest
javaservice:/home/guest/source/signal$ ls -alF
total 16
drwxr-xr-x 3 java java 4096 Jan 1 23:38 ./
drwxrwxrwx 6 guest users 4096 Jan 1 23:21 ../
drwxr-xr-x 3 java java 4096 Jan 1 23:20 org/
-rw-r--r-- 1 java java 488 Jan 1 23:38 org_jsn_signal_SignalTest.h
javaservice:/home/guest/source/signal# cat org_jsn_signal_SignalTest.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_jsn_signal_SignalTest */
#ifndef _Included_org_jsn_signal_SignalTest
#define _Included_org_jsn_signal_SignalTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_jsn_signal_SignalTest
* Method: signal
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_org_jsn_signal_SignalTest_signal
(JNIEnv *, jobject, jint, jstring);
#ifdef __cplusplus
}
#endif
#endif
javaservice:/home/guest/source/signal#
(....... vi signalhandle.c ....... )
javaservice:/home/guest/source/signal# ls -alF
total 48
4 drwxr-xr-x 3 java java 4096 Jan 2 01:38 ./
4 drwxrwxrwx 6 guest users 4096 Jan 2 01:33 ../
8 -rw-r--r-- 1 root root 4625 Jan 2 01:28 NativeStringUtil.c
4 -rw-r--r-- 1 root root 559 Jan 2 01:28 NativeStringUtil.h
4 -rw-r--r-- 1 root root 129 Jan 2 01:38 cc
4 drwxr-xr-x 3 java java 4096 Jan 1 23:20 org/
4 -rw-r--r-- 1 java java 488 Jan 2 01:35 org_jsn_signal_SignalTest.h
4 -rw-r--r-- 1 java java 829 Jan 2 01:37 signalhandle.c
javaservice:/home/guest/source/signal#
javaservice:/home/guest/source/signal# cat signalhandle.c
#include <signal.h>
#include "org_jsn_signal_SignalTest.h"
#include "NativeStringUtil.h"
/*
NOTE: global variable !!
*/
JNIEnv *g_env;
jobject g_obj;
jmethodID g_mid;
void signal_handle(signum)
int signum;
{
if ( g_mid == 0 ) return;
(*g_env)->CallVoidMethod(g_env, g_obj, g_mid, signum);
}
/*
* Class: org_jsn_signal_SignalTest
* Method: signal
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_org_jsn_signal_SignalTest_signal
(JNIEnv *env, jobject obj, jint signum, jstring handle_name)
{
jclass cls = (*env)->GetObjectClass(env, obj);
char *method = jbyteArray2cstr( env, javaGetBytes(env, handle_name) );
jmethodID mid = (*env)->GetMethodID(env,cls, method, "(I)V");
if ( mid == 0 ) return;
g_env = env;
g_obj = obj;
g_mid = mid;
signal(signum, signal_handle);
}
javaservice:/home/guest/source/signal# cat cc
#!/bin/sh
gcc -shared -I/usr/java/include \
-I/usr/java/include/linux \
-o libsignalhandle.so signalhandle.c NativeStringUtil.c
javaservice:/home/guest/source/signal# sh cc
javaservice:/home/guest/source/signal#
javaservice:/home/guest/source/signal# export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
javaservice:/home/guest/source/signal# export CLASSPATH=.:$CLASSPATH
javaservice:/home/guest/source/signal#
javaservice:/home/guest/source/signal# java org.jsn.signal.SignalTest
signal listening
waiting...0
waiting...1
waiting...2
....
이렇게 수행되고 있을 때, 다른 터미널에서 kill 명령어를 날려 봅니다.
[Terminal B]
javaservice:/root# ps -ef|grep java|grep root
root 17102 15065 2 01:00 pts/4 00:00:00 /usr/java/bin/linux/native_threa
root 17110 17102 0 01:00 pts/4 00:00:00 /usr/java/bin/linux/native_threa
root 17111 17110 0 01:00 pts/4 00:00:00 /usr/java/bin/linux/native_threa
root 17112 17110 0 01:00 pts/4 00:00:00 /usr/java/bin/linux/native_threa
root 17115 15768 0 01:00 pts/3 00:00:00 grep java
javaservice:/root# kill -2 17102
javaservice:/root# kill -3 17102
javaservice:/root# kill -15 17102
javaservice:/root# kill -10 17102
javaservice:/root# kill -9 17102
javaservice:/root#
결과는 다음과 같습니다.
javaservice:/home/guest/source/signal# java org.jsn.signal.SignalTest
signal listening
waiting...0
waiting...1
waiting...2
I have got signal : 2
I have got signal : 3
gracefully exited, really?
waiting...3
I have got signal : 15
gracefully exited, really?
waiting...4
I have got signal : 10
waiting...5
Killed
javaservice:/home/guest/source/signal#
이 프로그램은 Ctl-C 를 눌려도 종료되지 않습니다. signal 2 : SIGINT 가 잡히기
때문입니다. 오로지 다른 터미널 창에서 kill -9 를 날려야만 죽습니다.
주의할 점은 JNI의 callback 을 통해 수행되는 Java code 에서 System.exit() 을
날려도 JVM 이 exit 되지는 않습니다.
NOTE: signal 번호는 아래의 첨부 A 를 참조하세요.
따라서, 이와 같은 signal 처리는 단지 자바프로그램에서 process 의 signal 을
받아 무언가를 하도록 할 때 사용될 수 있을 뿐이며, "gracefully exiting" 을
이처럼 JNI의 callback 을 이용해 구현할 수는 없는 듯 합니다. 즉, Ctl-C 를
누른다거나, kill -9 를 제외한 kill -3 (SIGQUIT), kill -15(SIGTERM) 를 잡아채어
미진한 종료작업을 수행한 수 JVM이 내려가도록 하려면 뭔가 다른 방법을 강구해야
하는 듯 합니다.
JNI callback 을 사용하지 말아야 한다면, JNI 메소드를 부르되 signal 이
올 때까지 blocking 이 걸려있으면 될 듯 한데, 어디 한번 또 해 볼까요....
[두번째 예제] "gracefully exiting"
기본적인 개념은 별도의 자바 Thread 가 C native 코드를 호출하여 특정 signal 이
발생할 때 까지 기다리게 됩니다.
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# ls -alF
total 24
4 drwxr-xr-x 3 root root 4096 Jan 2 02:30 ./
4 drwxrwxrwx 7 guest users 4096 Jan 2 01:43 ../
4 -rw-r--r-- 1 root root 129 Jan 2 01:43 cc
4 drwxr-xr-x 3 root root 4096 Jan 2 01:43 org/
4 -rw-r--r-- 1 root root 269 Jan 2 02:26 signalhandle2.c
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# ls -alF org/jsn/signal/
total 24
4 drwxr-xr-x 2 root root 4096 Jan 2 02:03 ./
4 drwxr-xr-x 3 root root 4096 Jan 2 01:43 ../
4 -rw-r--r-- 1 root root 488 Jan 2 02:03 SignalMonitor.java
4 -rw-r--r-- 1 root root 505 Jan 2 02:00 SignalTest2.java
javaservice:/home/guest/source/signal2# cat org/jsn/signal/SignalMonitor.java
package org.jsn.signal;
public class SignalMonitor extends Thread
{
static {
try{
System.loadLibrary("signalhandle2");
}catch(Exception e){
e.printStackTrace();
}
}
public SignalMonitor(){
super();
}
public void run(){
signal_wait(15); // SIGTERM
System.out.println("I've got SIGTERM signal");
System.out.println("program terminated gracefully");
System.exit(1);
}
public native synchronized void signal_wait(int signum);
}
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# cat org/jsn/signal/SignalTest2.java
package org.jsn.signal;
public class SignalTest2 {
public SignalTest2() {
SignalMonitor monitor = new SignalMonitor();
monitor.start();
System.out.println("signal listening");
}
public void do_something(){
// infinite loop
for(int i=0; true ; i++) {
System.out.println("waiting..." + i);
try{Thread.sleep(1000 * 10);}catch(Exception e){}
}
}
public static void main(String[] args){
SignalTest2 test = new SignalTest2();
test.do_something();
}
}
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# javac org/jsn/signal/*.java
javaservice:/home/guest/source/signal2# ls -alF org/jsn/signal/
total 24
4 drwxr-xr-x 2 root root 4096 Jan 2 02:44 ./
4 drwxr-xr-x 3 root root 4096 Jan 2 01:43 ../
4 -rw-r--r-- 1 root root 813 Jan 2 02:55 SignalMonitor.class
4 -rw-r--r-- 1 root root 489 Jan 2 02:44 SignalMonitor.java
4 -rw-r--r-- 1 root root 953 Jan 2 02:55 SignalTest2.class
4 -rw-r--r-- 1 root root 505 Jan 2 02:00 SignalTest2.java
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# javah -jni org.jsn.signal.SignalMonitor
javaservice:/home/guest/source/signal2# ls -alF
total 40
4 drwxr-xr-x 3 root root 4096 Jan 2 02:05 ./
4 drwxrwxrwx 7 guest users 4096 Jan 2 01:43 ../
4 -rw-r--r-- 1 root root 129 Jan 2 01:43 cc
4 drwxr-xr-x 3 root root 4096 Jan 2 01:43 org/
4 -rw-r--r-- 1 root root 487 Jan 2 02:05 org_jsn_signal_SignalMonitor.h
4 -rw-r--r-- 1 root root 269 Jan 2 02:26 signalhandle2.c
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# cat org_jsn_signal_SignalMonitor.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_jsn_signal_SignalMonitor */
#ifndef _Included_org_jsn_signal_SignalMonitor
#define _Included_org_jsn_signal_SignalMonitor
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_jsn_signal_SignalMonitor
* Method: signal_wait
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_org_jsn_signal_SignalMonitor_signal_1wait
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# cat signalhandle2.c
#include <signal.h>
#include "org_jsn_signal_SignalMonitor.h"
int signal_reached = 0;
void signal_handle(signum)
int signum;
{
signal_reached = 1;
}
/*
* Class: org_jsn_signal_SignalMonitor
* Method: signal_wait
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_org_jsn_signal_SignalMonitor_signal_1wait
(JNIEnv *env, jobject obj, jint signum)
{
signal_reached = 0;
signal(signum, signal_handle);
while ( signal_reached == 0 )
;
/* CPU를 다소 점유할텐데, 더 좋은 방법 있나요? */
}
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# cat cc
#!/bin/sh
gcc -shared -I/usr/java/include \
-I/usr/java/include/linux \
-o libsignalhandle2.so signalhandle2.c
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# sh cc
javaservice:/home/guest/source/signal2# ls -alF
total 32
4 drwxr-xr-x 3 root root 4096 Jan 2 02:36 ./
4 drwxrwxrwx 7 guest users 4096 Jan 2 01:43 ../
4 -rw-r--r-- 1 root root 112 Jan 2 02:35 cc
8 -rwxr-xr-x 1 root root 5299 Jan 2 02:36 libsignalhandle2.so*
4 drwxr-xr-x 3 root root 4096 Jan 2 01:43 org/
4 -rw-r--r-- 1 root root 482 Jan 2 02:29 org_jsn_signal_SignalMonitor.h
4 -rw-r--r-- 1 root root 283 Jan 2 02:36 signalhandle2.c
이제 실행시켜 보겠습니다.
javaservice:/home/guest/source/signal2# java org.jsn.signal.SignalTest2
signal listening
waiting...0
waiting...1
......
실행되고 있는 중에 다른 Terminal 창에서 다음과 같이 SIGTERM(15) 을 날립니다.
[Terminal B]
javaservice:/root# ps -ef|grep java|grep root
root 19104 15065 4 02:50 pts/4 00:00:00 /usr/java/bin/linux/native_threa
root 19112 19104 0 02:50 pts/4 00:00:00 /usr/java/bin/linux/native_threa
root 19113 19112 0 02:50 pts/4 00:00:00 /usr/java/bin/linux/native_threa
root 19114 19112 0 02:50 pts/4 00:00:00 /usr/java/bin/linux/native_threa
root 19115 19112 99 02:50 pts/4 00:00:05 /usr/java/bin/linux/native_threa
root 19117 15768 0 02:50 pts/3 00:00:00 grep java
javaservice:/root# kill -15 19104
javaservice:/root#
결과는 다음과 같습니다.
javaservice:/home/guest/source/signal2# java org.jsn.signal.SignalTest2
signal listening
waiting...0
waiting...1
I've got SIGTERM signal
program terminated gracefully
javaservice:/home/guest/source/signal2#
원하시는 답변이 되었기를 바랍니다.
----------------------------------------------------------------------------------
2000.1.4 CPU 점거현상 보완 추가
CPU를 점거하는 문제를 해결하였습니다. 아래 문서를 참조하세요.
"CPU 점거현상 보완 추가"
http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=978538886
[첨부A signal.h : Linux]
/* Signals. */
#define SIGHUP 1 /* Hangup (POSIX). */
#define SIGINT 2 /* Interrupt (ANSI). */
#define SIGQUIT 3 /* Quit (POSIX). */
#define SIGILL 4 /* Illegal instruction (ANSI). */
#define SIGTRAP 5 /* Trace trap (POSIX). */
#define SIGABRT 6 /* Abort (ANSI). */
#define SIGIOT 6 /* IOT trap (4.2 BSD). */
#define SIGBUS 7 /* BUS error (4.2 BSD). */
#define SIGFPE 8 /* Floating-point exception (ANSI). */
#define SIGKILL 9 /* Kill, unblockable (POSIX). */
#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */
#define SIGSEGV 11 /* Segmentation violation (ANSI). */
#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */
#define SIGPIPE 13 /* Broken pipe (POSIX). */
#define SIGALRM 14 /* Alarm clock (POSIX). */
#define SIGTERM 15 /* Termination (ANSI). */
#define SIGSTKFLT 16 /* Stack fault. */
#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */
#define SIGCHLD 17 /* Child status has changed (POSIX). */
#define SIGCONT 18 /* Continue (POSIX). */
#define SIGSTOP 19 /* Stop, unblockable (POSIX). */
#define SIGTSTP 20 /* Keyboard stop (POSIX). */
#define SIGTTIN 21 /* Background read from tty (POSIX). */
#define SIGTTOU 22 /* Background write to tty (POSIX). */
#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */
#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */
#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */
#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */
#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */
#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */
#define SIGPOLL SIGIO /* Pollable event occurred (System V). */
#define SIGIO 29 /* I/O now possible (4.2 BSD). */
#define SIGPWR 30 /* Power failure restart (System V). */
#define SIGUNUSED 31
-------------------------------------------------------
본 문서는 자유롭게 배포/복사 할 수 있으나 반드시
이 문서의 저자에 대한 언급을 삭제하시면 안됩니다
================================================
자바서비스넷 이원영
E-mail: javaservice@hanmail.net
PCS:019-310-7324
================================================
|
댓글 없음:
댓글 쓰기