1.问题现象
组件反馈使用systemd控制进程后,相应的JAVA进程已启动,并且通过ps可以查看到进程,但是jps无法显示该进程的进程号,现象如下图所示
2. 问题分析
基于该问题现象,首先确认进程启动时使用的java和用户环境变量中java是否一致,经确认不存在使用不同路径下java版本差异问题,排除java版本不同的可能
既然用ps -ef|grep hps-agent能看到启动的java进程,但是用jps查看却不存在该进程的id,当然这种情况下jconsole、jvisualvm可能也无法监控该进程,其他java自带工具也可能无法使用。那要解决我们的问题首先要弄清楚jps如何显示当前用户已启动的java进程信息。下面介绍一下jps如何显示当前用户已启动java进程信息
1) jps的作用
jps类似linux的ps命令,不同的是ps是用来显示进程,而jps只显示java进程,准确的说是当前用户已启动的部分java进程信息,信息包括进程号和简短的进程command。
2) jps如何显示进程
java程序启动后,默认(请注意是默认)会在/tmp/hsperfdata_userName目录下以该进程的id为文件名新建文件,并在该文件中存储jvm运行的相关信息,其中的userName为当前的用户名,/tmp/hsperfdata_userName目录会存放该用户所有已经启动的java进程信息。对于windows机器/tmp用Windows存放临时文件目录代替。而jps、jconsole、jvisualvm等工具的数据来源就是这个文件(/tmp/hsperfdata_userName/pid),并且只会从这个目录下面读取该文件,所以当该文件不存在或是无法读取时就会出现jps无法查看该进程号,jconsole无法监控等问题
了解了以上原理,基本上已经心中有数,也就是说只要/tmp/hsperfdata_userName
目录下面生成该进程id的文件,那么jps应该就可以显示对应的进程信息。
分别验证两种方式,一种通过进程启动脚本拉起进程,对应的目录下生成了该id命名的文件,可以通过jps查看到进程信息,如下图所示
第二种通过systemd拉起进程后,jps查看不到进程信息,到对应目录查看发现该id文件确实不存在,所以此时jps无进程信息也就可以解释了。那么为什么没有生成该id文件?此时我们有必要了解一下使用systemd启动进程跟手工通过脚本拉起有什么差异?请看进程对应的service文件。
从上图文件可知,针对该进程配置了PrivateTmp属性,并且该属性值为true.对于通过systemd控制的进程来说,每个进程都会有PrivateTmp这么一个属性,用于设置是否使用私有的tmp目录。只要设置使用这个属性的service,都会使用私有的tmp目录。
到这里恍然大悟,原来进程对应的tmp目录不是jps默认读取的目录了,下面
我们来确认一下是否果真如此?如下图所示,进程拉起后在/tmp目录下,生成了/tmp/systemd-private-xxxxx-hps-agent.service-xxx 格式的linux目录,并且进入该目录,以进程id命名的文件按照默认的目录结构赤裸裸的躺在这里,静等别人来读取呢。
到此,该问题完全明确,都是进程tmp目录变化惹的祸。
3. 问题解决
针对想要通过jps查看进程,又希望通过systemd负责进程管理的情况,我们只需要修改对应的service配置,关闭PrivateTmp开关即可。
但是使用systemd管理进程,是不是我们都必须关闭这个开关,所有进程都使用共享的tmp目录呢,答案必须是否定的,要不然还设计这个配置干啥呢
4. 为何要使用PrivateTmp
/tmp目录一般是所有用户和所有service都共享的,对于所有用户和service来说,都会有写和读的权限。如下图所示,那么这里会存在安全性的问题。把各个service的tmp目录隔离开的话,可以保证一定的安全性。
对于每个service的tmp目录,会在服务启动(start)时创建该目录,并且在关闭(stop)服务时删除该目录。如果我们是使用自己定义的临时目录路径的话,就需要我们自己定义相关的维护机制了(如:定期删除这些临时文件等),而使用这个PrivateTmp的话,就不需要我们自己去维护了。
5. 其他jps无法显示进程的情况
从网上查询了几个jps无法显示进程的情况,一同分享。
1) 磁盘读写、目录文件权限问题
若该用户没有权限写/tmp目录或是磁盘已满,则无法创建/tmp/hsperfdata_userName/pid文件。或该文件已经生成,但用户没有读权限
2) 临时文件丢失,被删除或是定期清理
对于linux机器,一般都会存在定时任务对临时文件夹进行清理,导致/tmp目录被清空。常用的可能定时删除临时目录的工具为crontab、redhat的tmpwatch、ubuntu的tmpreaper等等。
3) Java进程信息文件存储地址被设置,不在/tmp目录下
上面我们在介绍时说默认会在/tmp/hsperfdata_userName目录保存进程信息,但由于以上1、2所述原因,可能导致该文件无法生成或是丢失,所以java启动时提供了参数(-Djava.io.tmpdir),可以对这个文件的位置进行设置,而jps、jconsole都只会从/tmp目录读取,而无法从设置后的目录读取信息。
6. 其他补充
/tmp/hsperfdata_userName/pid文件会在对应java进程退出后被清除。如果java进程非正常退出(如kill -9),那么pid文件会被保留,直到执行一次java命令或是加载了jvm程序的命令(如jps、javac、jstat),会将所有无用的pid文件都清除掉
7. 参考
Jsp介绍:http://download.oracle.com/javase/1.5.0/docs/tooldocs/share/jps.html
man
systemd.exec 查找PrivateTmp关键字
https://lists.centos.org/pipermail/centos/2015-April/151589.html
http://0pointer.de/blog/projects/security.html