Contenu connexe Similaire à Shell,信号量以及java进程的退出 (20) Plus de wang hongjiang (9) Shell,信号量以及java进程的退出3. 第一个例子
• public class Daemon {
• public static void main(String[] args) {
• new Thread() {
• public void run() {
• while (true) {
• try {
• // doSomething(); 该线程周期性的做一些事
情
• Thread.sleep(2000);
• } catch (Exception e) {
• }
• }
• }
• }.start();
• }
• }
18. Kill
注意在大多数shell 下它同时也是一个内置命令
$ echo $0 && type -a kill
-bash
kill is a shell builtin
kill is /usr/bin/kill
➜ echo $0 && type -a kill
zsh
kill is a shell builtin
kill is /bin/kill
% echo $0 && type -a kill
csh
kill is a shell builtin
kill is /bin/kill
24. 守护进程对SIGHUP的处理
$ kill –s HUP pidof_nginx
“This signal [SIGHUP] is commonly used to notify
daemon processes (Chapter 13) to reread their
configuration files. The reason SIGHUP is chosen for
this is because a daemon should not have a
controlling terminal and would normally never
receive this signal.”
<<Advanced Programming in the UNIX Environment>>
30. JVM如何产生coredump?
第一种方式,发送 SIGILL 或 SIGBUG 信号,jvm crash前会产生coredump
# 启动java前需要去掉限制
$ ulimit -c unlimited
$ java Daemon
# 发送 SIGILL 或 SIGBUS 信号给目标 java 进程
$ kill –s ILL `pgrep java` 等同于 kill -4
或
$ kill –s BUS `pgrep java` 等同于 kill -7 (Mac下等同于 kill -10)
# 产生的coredunp 文件可在 hs_err_pidxxx.log里查找
$ grep 'Core dump' hs_err_pid37418.log
Core dump written. Default location: /cores/core or core.37418
# 如果目标进程没有unlimited 可能会无法生成 coredump 文件
31. JVM如何产生coredump?
第二种方式,通过gdb来产生coredump, jvm不会crash
# 使用 gdb attach 到目标进程
$ sudo gdb --pid=19936
...
(gdb) gcore /tmp/jvm.core
warning: target file /proc/19936/cmdline contained unexpected null characters
Saved corefile /tmp/jvm.core
(gdb) detach
(gdb) quit
#coredump文件可以作为源,再进行heapdump,比直接用 jmap对目标进程 heapdump更快
$ jmap -dump:format=b,file=jvm.hprof /usr/bin/java /tmp/jvm.core
33. SIGSTOP
scala> var a=0;
scala> while(true) { Thread.sleep(2000); println(a); a=a+1 }
0
1
2
$ ps -ostat -p `pidof java`
STAT
S+
$ kill -s stop `pidof java`
此时jvm进程被暂停住,进程状态也变为: T (TASK_STOPPED or TASK_TRACED),暂停状态或跟踪状态
$ ps -ostat -p `pidof java`
STAT
T+
34. SIGCONT
$ kill -s cont `pidof java`
$ ps -ostat -p `pidof java`
STAT
S+
java进程恢复运行状态,继续循环输出
36. SIGUSR1
$ kill –s USR1 pidof_nginx
Logrotate
$ kill –s USR1 pidof_java
Jvm 退出
$ kill –s USR2 pidof_java
被识别为 SIGSEGV, Jvm Crash (Coredump)
51. SIGKILL
$ ulimit -t 10
$ scala
scala> while(true){}
# 在另一个终端监听 java收到的signal
$ sudo strace -e trace=signal -p 27708
Process 27708 attached
+++ killed by SIGKILL +++
53. JVM Signal Handler
scala> import sun.misc._
scala> Signal.handle(new Signal("INT"),
| new SignalHandler(){
| def handle(sig:Signal){ println("down") }}
| )
$ kill -s INT `pidof java`
scala> down // 被捕获,进程不会退出
54. JVM Signal Handler
注意,被OS或JVM已注册了的 Signal 不能被修改,比如 SIGQUIT、
SIGUSR1 等
scala> Signal.handle(
| new Signal("QUIT"),
| new SignalHandler(){
| def handle(sig:Signal){ println("down") }
| }
| )
java.lang.IllegalArgumentException: Signal already used by VM or OS:
SIGQUIT
at sun.misc.Signal.handle(Signal.java:166)
... 38 elided
65. 后台任务
#!/bin/bash
sleep 5 & # 模拟并行任务1
pid1=$!
sleep 5 & # 模拟并行任务2
pid2=$!
sleep 5 & # 模拟并行任务3
pid3=$!
wait $pid1 $pid2 $pid3 && echo "all done"
68. 交互式shell下的作业控制
$ vim
# 在 vim 下执行 ctrl-z
# vim 收到 SIGTSTP 信号,进程暂停并被移到后台
$ jobs
[1] + suspended vim
$ fg %1
# vim 收到 SIGCONT 信号,进程恢复并切换回前台执行
Sigtstp 与 Sigstop 类似,通常 sigtstp 是来自键盘 ( ctrl-z )
82. 1) 第一个终端下(bash)执行 sleep 4400
$ sleep 4400
2) 在第二个终端下监听 sleep 4400 进程的 singal
$ ps xf | grep -C2 sleep
24327 pts/2 Ss 0:00 _ -bash
24361 pts/2 S+ 0:00 _ sleep 4400
$ sudo strace -e trace=signal -p 24361
3) 用鼠标关闭第一个终端窗口
SIGHUP的链式反应
83. SIGHUP的链式反应
bash的子进程也收到 SIGHUP
$ sudo strace -e trace=signal -p 24361
Process 24361 attached
--- SIGHUP {si_signo=SIGHUP, si_code=SI_KERNEL,
si_value={int=1004881496, ptr=0x7f123be54658}} ---
+++ killed by SIGHUP +++
85. 回顾最初的例子:
$ java Daemon &
它在交互式shell下启了一个后台任务,终端
断开后(网络断开) shell 收到了 kernel 的
sighup 执行退出,并将 sighup 发送给了它的
子进程,java 进程收到 sighup 后也退出。
87. 摆脱 SIGHUP
1)nohup
$ nohup java Daemon &
2) disown
$ java Daemon &
$ disown
3) setsid 设置为一个新的 session
$ setsid java Daemon &
91. 摆脱 SIGHUP: subshell
$ ( java Daemon &)
在当前shell下额外起来一个新的subshell作为java的父
进程,等同于在当前shell下执行下面的脚本:
$ cat start.sh
#!/bin/sh
java Daemon &
$ ./start.sh
95. 将 catalina.sh start 简化:
#!/bin/sh
eval '"/pathofjdk/bin/java"’ 'params'
org.apache.catalina.startup.Bootstrap start '&’
脚本运行后 java 进程会挂到 pid为1的进程下
97. • 用 ctrl-c 终止当前test.sh进程时,系统events
进程向 java 和 tail 两个进程发送了SIGINT 信
号
• SIGINT [ 0 11 ] -> [ 0 20629 tail ]
• SIGINT [ 0 11 ] -> [ 0 20628 java ]
• SIGINT [ 0 11 ] -> [ 0 20615 test.sh ]
98. • 关闭ssh终端窗口时,sshd向下游进程发送SIGHUP,
为何java进程也会收到?
• SIGHUP [ 0 11681 sshd: hongjiang [priv] ] -> [ 57316
11700 bash ]
• SIGHUP [ 57316 11700 -bash ] -> [ 57316 11700 bash ]
• SIGHUP [ 57316 11700 ] -> [ 0 13299 tail ]
• SIGHUP [ 57316 11700 ] -> [ 0 13298 java ]
• SIGHUP [ 57316 11700 ] -> [ 0 13285 test.sh ]
Notes de l'éditeur 只有前台作业中的进程才能够从控制终端读取输入。这个限制条件避免了多个作业竞争读取终端输入。如果后台作业尝试从终端读取输入,就会接收到一个SIGTTIN信号。SIGTTIN信号的默认处理动作是停止作业。 一台大型主机往往需要支持许多用户同时使用,每个用户所使用操作的设备,就叫做Termial——终端,终端使用通信电缆与电脑主机连接,甚至可以通过电信网络(电话、电报线路等等)连接另一个城市的电脑。 照片来自:带你逛西雅图活电脑博物馆(五) - 古董电脑室 - 知乎专栏
Console 源自管风琴的控制台,在钢琴方面 根据高度不同,小型成为 spinet, 中型的是 console ,大型或专业型是 studio
控制进程通常就是 shell,
SIGHUP 引发的连锁反应,