Runtime.exec() 에 관하여
현재 환경입니다.
OS : Solaris 8
Servlet Container : Tomcat 5.0.25
JRE : Sun 1.4.2_05-b04
문제상황: 톰캣의 서블릿에서 외부 프로그램 (C로 되어 있으며 소켓통신함) 을 실행하는 부분이 있습니다. 헌데 톰캣의 file descriptor 갯수가 계속 증가하는 상황입니다. 외부프로그램은 문제없이 실행 후 종료되고 있구요. lsof -p [tomcat pid]로 확인해보니 증가하는것들은 전부 FIFO 입니다. 아마 Runtime.exec() 할때 생성되는 파이프들이 계속 잔존해서 그런것 같습니다. 그래서, 몇가지 테스트도 해보고 있습니다만... 관련해서 몇가지 복합적인 질문을 드리고자 합니다.
1. 톰캣에서 사용하는 fd 갯수를 /proc/[tomcat pid]/fd 아래의 fd갯수를 헤아리는 식으로 10분마다 로깅하고 있는데요. 특이할만한 점이 매시 정각이 되면 어떤 기본갯수(현재 23개)로 줄었다가 다시 늘어나는데 그 이전 늘어나는 추세와 맞추어 늘어난다는것입니다. 아래와 같은 식인데요. 정각이 되면 줄어드는 이유와, 다시 그 전 늘어나는 갯수와 맞추어 늘어나는 이유가 무엇인지요?
Fri Sep 9 12:00:00 KST 2005 | 23 Fri Sep 9 12:10:00 KST 2005 | 23 Fri Sep 9 12:20:00 KST 2005 | 23 Fri Sep 9 12:30:00 KST 2005 | 815 Fri Sep 9 12:40:00 KST 2005 | 815 Fri Sep 9 12:50:01 KST 2005 | 818 Fri Sep 9 13:00:00 KST 2005 | 23 Fri Sep 9 13:10:00 KST 2005 | 23 Fri Sep 9 13:20:00 KST 2005 | 834 Fri Sep 9 13:30:00 KST 2005 | 23 Fri Sep 9 13:40:01 KST 2005 | 23 Fri Sep 9 13:50:00 KST 2005 | 830 Fri Sep 9 14:00:00 KST 2005 | 23 Fri Sep 9 14:10:00 KST 2005 | 23 Fri Sep 9 14:20:00 KST 2005 | 23 Fri Sep 9 14:30:00 KST 2005 | 23 Fri Sep 9 14:40:00 KST 2005 | 830 Fri Sep 9 14:50:00 KST 2005 | 830 Fri Sep 9 15:00:00 KST 2005 | 23 Fri Sep 9 15:10:00 KST 2005 | 834 Fri Sep 9 15:20:00 KST 2005 | 23 Fri Sep 9 15:30:00 KST 2005 | 23 Fri Sep 9 15:40:00 KST 2005 | 836 Fri Sep 9 15:50:01 KST 2005 | 836
2. 간단한 테스트 프로그램을 짜서 해보니 자바에서 Runtime.exec() 할때 최초 PIPE가 6개 생겼다가 곧 3개로 줄어든 상태에서 외부프로그램이 종료되어도 그대로 3개가 있다가 자바가 종료된 후에야 사라지더군요. Runtime.exec()이 동작이.. 부모프로세스 fork하고 거기서 자식 fork해서 외부프로그램 실행되고 부모는 죽어서 사라져서 갯수가 6개 되었다가 곧 3개되는것 같은데요. 이 말이 맞는지요? 조금 설명해 주시면 감사.. 그리고 input/output때문에 PIPE가 2개는 필요할텐데 추가 한개는 어디에 사용되는 PIPE인지요? 아 쓰고나니 STDIN, STDOUT, STDERR 겠군요...
3. 외부 프로세스가 종료되어도 PIPE가 계속 남아 있다가 java 프로그램이 종료된 후에야 사라지더군요. 왜 PIPE가 남아있나요? 그게 inputStream, outputStream을 위해 죽지 않고 남아 있는것 같기도 하고.. 생성된 Process로부터 inputStream을 받아 뿌려보면 그때그때 외부프로그램의 output이 뿌려지는게 아니라 종료된 후에야 한꺼번에 readline()이 먹히더군요. 아 이게 BufferedReader라서 실제로는 그때그때 받아오는데 buffered되어서 나중에 한꺼번에 된듯 하기도 하군요. 여튼... Process.destroy()를 해주니깐 (이미 외부 프로그램은 종료된 상태구요) PIPE들이 샥 사라지더군요. 왜 이렇게 해놓았는지요? 만약 destroy를 호출안하면 언제 없어지나요? GC때 없어질까요? 이게 톰캣에서 23개로 정각마다 초기화되는거랑 관계가 있을까요? 이게 특정 OS와 관련이 있는 현상인가요? 아님 원래 Runtime.exec() 후에는 destroy()를 해줘야 하는건지요.
sun에 유사한 버그리포트가 있긴 합니다만.
http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4843136
헌데 destroy()를 할려면 외부프로세스가 종료된 후라야 하는데 그럴려면 waitFor()를 하여 대기해줘야 하고..그럼 서블릿에서 다음단계일을 안하고 불필요하게 대기해줘야 하니깐 그것도 좋지못한것 같네요.
아..쓰다보니 질문이 좀 장황스럽게 되어 버렸네요.. 큰 맥락은 하나입니다만.
안바쁘실때 답변 달아주시면 감사하겠습니다.
저는 테스트 해 볼 여건이 아닌지라 추측으로 답변을 드리면... 좀비가
저는 테스트 해 볼 여건이 아닌지라 추측으로 답변을 드리면... 좀비가 생기는 듯 합니다. 수행 후에 Process를 다시 null로 초기화하는 코드가 있다면 Process.exitValue()로 exit 값을 확인한 후에 하는 것을 테스트 해 보길 권합니다.
저도 결과가 궁금해서 그러는데, 다른 OS에서도 테스트 해보셨다면 결과를 알려 주면 고맙겠습니다.
----
I paint objects as I think them, not as I see them.
atie's minipage
linux 2.6.8 에서도 마찬가지 결과를 보입니다.그리고 java
linux 2.6.8 에서도 마찬가지 결과를 보입니다.
그리고 java 1.4.2-09 에서도 마찬가지구요.
좀더 찾아보니깐... bugs.sun.com에도 이미 거론된바 있어보이는데 결국 명시적으로 destroy()를 하던지 3개의 파이프를 close하던지 하는게 더 안전하므로 그렇게 코딩해라는 것으로 보입니다.
이현상은 1.4.2 부터 생긴것 같은데요.. 1.3.1 에선 확실히 없었다는 코멘트가 있고.. 1.4.1에선 아직 테스트 안해봤습니다만, 저희 제품이 1.4.1에선 이런 (too many open files) 현상이 없었기 때문에 1.4.1에서도 괜찮았을것 같네요.
그럼 결국 서블릿이던 멀티쓰레드던, 싱글쓰레드던간에 Runtime.exec()후에는 waitFor()해서 블럭되어 있다가 종료후 destroy()를 해주는 과정을 거쳐야 하는가 보네요. 아 그리고 process를 null로 해주는건 아무 효과가 없었습니다.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692
waitFor()가 thread에 먹히는 것이 아니라 Process가 종
waitFor()가 thread에 먹히는 것이 아니라 Process가 종료될 때까지 기다린다고는 알고 있었고, Process null 언급한 것은 exitValue()가 0이면 destroy()를 하면 좀비가 되는 것을 막지 않을까 싶어 이야기 드린 겁니다.
gcj 3.0에서 waitFor() 패치를 posix-thread에 오래 전에 했다는 글을 봤고 위의 버그 리포트에 레드햇 7.2에서는 통과했다는데. 2.6.8에서도 마찬가지라는 것은 의아하군요.
----
I paint objects as I think them, not as I see them.
atie's minipage
댓글 달기