日期:2014-05-16  浏览次数:20609 次

jsvc 启动java 在linux下的实现原理

jsvc 是在apache的daemon项目下开源项目,主要功能可以使一些运行在普通用户下的java进程获取一些root权限下的权利,比如端口在1024下等。


如何运行

在自己的java代码中,实现start, init , stop,destroy的方法,将自己的编译打成jar文件,  通过调用jsvc 来启动

./jsvc -java-home  /usr/java/jdk1.7.0_09/ -user nobody  -pidfile /opt/apache-tomcat_1/logs/catalina-daemon1.pid -wait 10 -errfile "/tmp/error" -outfile "/tmp/output" -debug -classpath /root/test.jar:/root/commons-daemon.jar test 

几个注意点:

commons_daemon.jar文件是用于调用你的class文件,

关于-java-home, 在这里有一个bug(https://issues.apache.org/jira/browse/DAEMON-268),哪怕指定,也会指定为默认的/usr/java 

其他的请参考jsvc --help


实现原理

jsvc 是一个源码是c的程序,通过fork出子进程去启动java,而进程成为控制进程,可以实现监视java子进程的目地。

改变启动虚拟机的进程的用户id, 和用户组

通过调用setgid,setuid来改变当前进程的用户id,和组,这里要注意的是当改变用户id,和组的时候,当前进程会改变进程的capability, 所以需要reset 进程的capability。

查看进程的capability 可以通过调用内核调用 __NR_capget / __NR_capset 的方式

static int get_legacy_caps(){
        struct __user_cap_header_struct caphead;
        struct __user_cap_data_struct   cap;
        memset(&caphead, 0, sizeof caphead);
        caphead.version = LEGACY_CAP_VERSION;
        if (syscall(__NR_capget, &caphead, &cap) < 0)
                log_error("capget failed: %m");
        log_debug("PID is %d print the cap  0x%x, 0x%x, 0x%x\n", getpid(), cap.effective, cap.permitted, cap.inheritable);
        return 0;
}


启动java

通过调用JNI_CreateJavaVM 启动虚拟机器,同时调用包common-daemon里的DaemonLoader class, 调用你所写的类中的start,...这些方法。


碰到的问题

在jsvc 里在启动java以后就将jvm的虚拟机的进程的capability 设置成了0,  导致在虚拟机里的创建线程受到max process 的控制, ulimit -u 

已经创建issue: https://issues.apache.org/jira/browse/DAEMON-270, 短期解决办法可以设置ulimit 到比较大的值