個人進程去哪兒了,誰殺了個人進程

  

  曾經在我眼前,卻又消失不見,這是今天的第六遍。html

  一段感情,咱們但願它可以天長不久,越穩定越好,最怕的就是中途夭折,無疾而終。python

  即便不能到海枯石爛的一天,咱們也但願好聚好散,你們理智的告別,而後笑着活下去。linux

  

  何況,咱們時候,咱們只是但願給這段感情按下一個暫停鍵,你們先冷靜思考本身的問題,而後從新繼續。程序員

  最讓人抓狂的是忽然就聯繫不上另外一半,不接你電話、不回你短信、消息在你的世界。而你根本不知道緣由,多是由於在約會的時候你沒有誇她新買的鞋子漂亮;也許是你多看了路邊的美眉兩眼;也許是出現了更強有力的競爭者;也許是她的父母以爲程序員太屌絲,不肯意大家繼續來往;也許是她得了不治之症,不想連累你。總之,你不知道哪裏出了問題,惶惶不可終日。ubuntu

  

  好吧,這裏不是情感專欄,我是一個程序員,咱說點有關服務器程序的事情,準確一點,是指服務端進程,後文不加區分。bash

 

  服務端的程序,通常都但願比較長時間的運行,好比7*24小時。不過,程序也須要更新,好比修Bug,好比增長新功能,好比修復增長新功能引入的bug。服務器

  一個大型系統通常包含多個進程,同一個服務也多是有多個進程組成,那麼能夠將這組進程逐步更新:先讓一部分進程中止提供服務,等待已有的請求都完畢以後,重啓這些服務,而後再更新替他進程。app

  即便,咱們須要關閉全部服務,也須要優雅(graceful)地中止這些進程。所謂的優雅,就是保證已有的請求都能處理完,須要持久化的狀態、數據都保存成功,而後再結束進程,通常來講,能夠經過發送信號或者經過哨兵(sentinel)來結束。框架

  只要是預期內的進程結束,那麼都是ok的。而預期以外的進程結束每每令程序員抓狂,線上服務器的問題每每意味着分分鐘幾位數的損失、KPI、年終獎......想一想就很恐怖。運維

  本文地址:http://www.cnblogs.com/xybaby/p/8098229.html

Linux Signal

  一個進程的異常終止,一般有兩種狀況,一種是crash,另外一種是被kill掉了。

  crash是指程序出現了本身沒法解決的異常狀況,只能終止,好比Python語言拋出了一個未被捕獲的異常,就會結束程序。對於C、C++,最有名的就是段錯誤(segmentation fault),若是在Linux下面,那麼會生成coredump,程序員經過gdb(有可能)能夠分析出crash的緣由。固然,要生成coredump也是須要正確的設置,能夠經過ulimit(ulimit -c)查看或者設置。

  而進程被kill掉,就是其餘進程給目標進程發送了信號(signal),固然也能夠是本身給本身發的信號,而目標進程沒有正確處理這些信號,或者根本沒有機會(權力)處理這些信號,那麼目標進程就有可能會終止。

  信號是Unix-like系統進程間通訊(IPC)的一種方式,這種通知是異步的,信號是一種軟中斷,操做系統會將目標進程的正常執行流程暫停,而後處理信號。若是目標進程註冊了相應的信號處理函數(signal handler),那麼就會調用這個signal handler,不然會執行默認的信號處理函數。

  不一樣的操做系統,支持的信號可能略有差別,可使用kill -l 查看系統全部的信號。下面是Linux上常見的信號以及處理機制

  信號    值 處理動做 發出信號的緣由 
  ---------------------------------------------------------------------- 
  SIGHUP  1   A   終端掛起或者控制進程終止 
  SIGINT   2   A   鍵盤中斷(如break鍵被按下) 
  SIGQUIT   3   C   鍵盤的退出鍵被按下 
  SIGILL    4   C   非法指令 
  SIGABRT  6   C   由abort(3)發出的退出指令 
  SIGFPE    8   C   浮點異常 
  SIGKILL  9   AEF  Kill信號 
  SIGSEGV   11   C  無效的內存引用 
  SIGPIPE  13   A  管道破裂: 寫一個沒有讀端口的管道 
  SIGALRM 14    A  由alarm(2)發出的信號 
  SIGTERM 15    A  終止信號 
  SIGUSR1  30,10,16 A  用戶自定義信號1 
  SIGUSR2  31,12,17 A  用戶自定義信號2 
  SIGCHLD  20,17,18 B    子進程結束信號 
  SIGCONT  19,18,25       進程繼續(曾被中止的進程) 
  SIGSTOP  17,19,23 DEF 終止進程 
  SIGTSTP  18,20,24 D 控制終端(tty)上按下中止鍵 
  SIGTTIN   21,21,26 D 後臺進程企圖從控制終端讀 
  SIGTTOU 22,22,27 D 後臺進程企圖從控制終端寫 

  處理動做一項中的字母含義以下 

  A  缺省的動做是終止進程
  B  缺省的動做是忽略此信號
  C  缺省的動做是終止進程並進行內核映像轉儲(dump core)
  D  缺省的動做是中止進程
  E  信號不能被捕獲
  F  信號不能被忽略

  若是默認處理動做是C(coredump),那麼就會生成coredump,而後終止進程,在上一篇文章《啊,個人程序爲啥卡住啦》中,提到用kill -11 pid 來終止、調試卡住的程序,這個11就是指信號SIGSEGV

  注意SIGKILL SIGSTOP這兩個信號,既不能夠被捕獲,也不能被忽略,就是說收到這兩個信號,程序就會不留痕跡地終止。

究竟是什麼信號

  從上面,咱們能夠看到,有不少信號均可以終止進程,若是咱們沒有針對某種信號指定處理函數,那麼咱們怎麼知道進程是被哪個進程kill掉了呢,那就是strace

  咱們以一段簡單的Python代碼爲例:

1 # -*- coding: utf-8 -*-
2 
3 def func():
4     while True:
5         pass
6 
7 if __name__ == '__main__':
8     func()

  運行該代碼: 

python run_forever.py &
[1] 1035

  能夠看到pid是1035,咱們使用strace查看該進程:$ strace -T -tt -e trace=all -p 1035, 而後另起終端kill這個Python進程: $kill -9 1035。在strace能夠看到:

$ strace -T -tt -e trace=all -p 1035
Process 1035 attached
16:19:52.092202 +++ killed by SIGKILL +++

  也可使用其餘信號kill,好比,從新運行Python程序,進程爲32599, kill -10 32599,那麼strace的結果是

$strace -T -tt -e trace=all -p 32599
Process 32599 attached
16:09:20.264993 --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=29373, si_uid=3010} ---
16:09:20.265656 +++ killed by SIGUSR1 +++

  對比kill -9, kill -10多了一行信息,這是很是有用的信息,由於有了兩個很是重要的字段,si_pid、 si_uid

pid_t si_pid; // sending process ID
pid_t si_uid; // Real user ID of sending process

  即si_pid說明是哪個進程發送的信息,si_uid是哪個用戶發出的信息,使用id si_uid就能看出詳細的用戶信息。這樣就直接定位了誰發送的信息。

 

  不過,不少時候,進程被幹掉,都是使用了kill -9(SIGKILL),那麼即便使用strace也只是知道被幹掉了,也無法知道被誰幹掉了。並且,除非一個程序常常被kill掉,不然不多有人有strace長期監控這個進程。

OOM

  關於進程收到了SIGKILL信號,有一種不得不提的狀況,那就是OOM(out of memory),簡單來講,就是當Linux系統內存不足,在大量使用swap以後,會kill掉內存佔用最大的進程。這應該算操做系統系統自身的一種保護機制,以防更多的進程不能正常工做。關於OOM killer,網上有詳盡的資料介紹,在這裏只是簡單看看現象。

  下面是一個簡單的Python程序,該程序會無限的分配內存,直到內存不足:

1 # -*- coding: utf-8 -*-
2 
3 def func():
4     l = [1]
5     while True:
6         l.append(l * 2)
7 
8 if __name__ == '__main__':
9     func()

   運行該程序,而後用strace追蹤,前面都是正常的內存分配相關的系統調用,直到最後

16:02:19.192409 mprotect(0x7f4aa886b000, 552960, PROT_READ|PROT_WRITE) = 0 <0.000014>
16:02:19.744000 mmap(NULL, 552960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory) <0.000108>
16:02:19.755545 mprotect(0x7f4aa88f2000, 552960, PROT_READ|PROT_WRITE) = 0 <0.000019>
16:02:23.404805 mmap(NULL, 552960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 <ptrace(SYSCALL):No such process>
16:02:25.325272 +++ killed by SIGKILL +++
[1]+ Killed python will_be_killed.py

   stackoverflow上who-killed-my-process-and-why一文指出,因爲outofmemory被kill掉的進程,會在/var/log下的某個文件中留下最終的遺蹟,在筆者使用的debian系統中,能夠經過dmesg查看:

  dmesg -T | grep -E -i -B100 'killed process'

  日誌以下:

 
[月 12日 24 16:02:24 2017] [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name
[月 12日 24 16:02:24 2017] [ 172] 0 172 10553 722 23 1375 0 systemd-journal
[月 12日 24 16:02:24 2017] [ 181] 0 181 10201 398 22 134 -1000 systemd-udevd
[月 12日 24 16:02:24 2017] [ 518] 0 518 8738 361 21 70 0 cron
[月 12日 24 16:02:24 2017] [ 519] 0 519 4756 314 13 45 0 atd
[月 12日 24 16:02:24 2017] [ 520] 0 520 13795 412 31 143 -1000 sshd
[月 12日 24 16:02:24 2017] [ 525] 0 525 4964 161 14 65 0 systemd-logind
[月 12日 24 16:02:24 2017] [ 536] 105 536 10531 172 26 98 -900 dbus-daemon
[月 12日 24 16:02:24 2017] [ 580] 0 580 1064 362 8 34 0 acpid
[月 12日 24 16:02:24 2017] [ 602] 0 602 4926 373 13 37 0 agetty
[月 12日 24 16:02:24 2017] [ 605] 0 605 4881 373 14 37 0 agetty
[月 12日 24 16:02:24 2017] [ 643] 108 643 8346 283 22 134 0 ntpd
[月 12日 24 16:02:24 2017] [ 889] 104 889 12794 369 26 158 0 exim4
[月 12日 24 16:02:24 2017] [11640] 0 11640 4188 1118 15 0 0 atop
[月 12日 24 16:02:24 2017] [29370] 0 29370 14434 420 30 177 0 sshd
[月 12日 24 16:02:24 2017] [29372] 3010 29372 14434 189 29 147 0 sshd
[月 12日 24 16:02:24 2017] [29373] 3010 29373 7813 491 20 587 0 bash
[月 12日 24 16:02:24 2017] [30731] 0 30731 14434 429 30 177 0 sshd
[月 12日 24 16:02:24 2017] [30733] 3010 30733 14434 328 29 154 0 sshd
[月 12日 24 16:02:24 2017] [30734] 3010 30734 7810 432 19 606 0 bash
[月 12日 24 16:02:24 2017] [30746] 3010 30746 13967 408 30 102 0 su
[月 12日 24 16:02:24 2017] [30747] 0 30747 7389 422 19 194 0 bash
[月 12日 24 16:02:24 2017] [31688] 0 31688 13967 408 31 101 0 su
[月 12日 24 16:02:24 2017] [31689] 3010 31689 7808 482 19 566 0 bash
[月 12日 24 16:02:24 2017] [32128] 3010 32128 7761 445 19 32 0 top
[月 12日 24 16:02:24 2017] [32132] 3010 32132 2357921 1868878 4581 467803 0 python
[月 12日 24 16:02:24 2017] [32133] 3010 32133 1255 152 7 57 0 strace
[月 12日 24 16:02:24 2017] Out of memory: Kill process 32132 (python) score 957 or sacrifice child
[月 12日 24 16:02:24 2017] Killed process 32132 (python) total-vm:9431684kB, anon-rss:7473936kB, file-rss:1576kB 

  因爲進程使用內存最多,並且沒有設置保護機制,因此32122這個python進程就被kill掉了。

  因此,若是機器內存使用率較高,那麼當進程消失的時候不妨用dmesg看看。

誰發的信號

  更廣泛的,即便咱們知道進程被SIGKILL幹掉了,這沒有什麼用。關鍵是得找出誰發送的這個信號,是故意的仍是意外,也許是新手寫的腳本誤傷,也許是老手故意搞破壞。

  最簡單的,那就是查看lasthistory,last看看誰登錄到了系統,而後再用history看看相關的操做記錄。但是,操做記錄是能夠被清除的,history -c能夠清除本終端上的操做記錄,也能夠直接清空、刪除.bash_history,也許有更高級的手段來禁止清空操做記錄,但究竟是道高一尺魔高一丈,仍是魔高一尺道高一丈,我也就不清楚了。

systemtap

  在who-killed-my-process-and-why中,指出了其中的一種辦法,使用systemtap,筆者也是第一次據說這個傢伙,聽說很是強大。官方給出了捕獲信號的示例代碼

probe begin
{
  printf("%-8s %-16s %-5s %-16s %6s %-16s\n",
         "SPID", "SNAME", "RPID", "RNAME", "SIGNUM", "SIGNAME")
}

probe signal.send 
{
  if (sig_name == @1 && sig_pid == target())
    printf("%-8d %-16s %-5d %-16s %-6d %-16s\n", 
      pid(), execname(), sig_pid, pid_name, sig, sig_name)
}

 

  將上述代碼保存爲sigmon.stp文件,而後查看待監控的進程的pid,使用下述命令監控發送到該進程的SIGKILL信號

stap -x pid sigmon.stp SIGKILL

   不過這種方法,我並無實驗成功,systemtap的安裝太麻煩了。。。

audit

  在Who sends a SIGKILL to my process mysteriously on ubuntu server中,提到了另一個更加簡單的方法,那就是使用audit

  安裝很簡單:sudo apt-get install auditd

  啓動服務並查看狀態: service auditd start & service auditd status

  而後經過auditctrl添加規則: auditctl -a exit,always -F arch=b64 -S kill -F a1=9

  啓動而後kill掉Python程序

$ python run_forever.py &
[1] 24067
$ kill -9 24067

  使用ausearch搜索結果: ausearch -sc kill。結果以下 

time->Mon Dec 25 19:52:55 2017
type=PROCTITLE msg=audit(1514202775.088:351): proctitle="bash"
type=OBJ_PID msg=audit(1514202775.088:351): opid=24067 日uid=-1 ouid=3010 oses=-1 ocomm="python"
type=SYSCALL msg=audit(1514202775.088:351): arch=c000003e syscall=62 success=yes exit=0 a0=5e03 a1=9 a2=0 a3=7ffc0d9f7b90 items=0 ppid=1349 pid=1350  uid=3010 gid=3010 euid=3010 suid=3010 fsuid=3010 egid=3010 sgid=3010 fsgid=3010 tty=pts0 comm="bash" exe="/bin/bash" key=(null)

 

   能夠看到,信號的目標進程是一個python程序,pid是24067,啓動該進程的用戶的id是3010。kil進程的信號被用戶3010在pid爲1350的bash中發出。

案例與總結

  我遇到過的,進程悄無聲息消失的狀況,有如下幾種

  (1)進程確實是crash了,不過用於core file size設置的問題,沒有生成coredump,這裏能夠經過ulimit -c確認

  (2)oom,代碼bug致使進程佔用內存過多,被操做系統幹掉

  (3)進程被父進程,或者監控進程(watchdog)給kill掉,這個在使用框架的時候容易出現

  (4)進程被誤殺,諸如這樣的腳本 kill -9 `ps aux | grep python | awk '{print $2}'`, 殺掉全部的python進程,這是很是粗暴的方法,很是容易誤殺

  (5)top,top命令也能殺掉進程,are you kidding me? No

  

  如上圖所示,進程9603是一個Python程序,top -c默認按照CPU使用量排序,因此這個CPU 100%的進程在最前面。當按下K鍵的時候,就會給進程發信號,default pid就是在第一行的進程,固然這裏也能夠輸入pid。直接回車,結果以下圖

  

  能夠看到,默認的信號是SIGTERM(15),也能夠輸入信號。在敲回車以後,這個進程就被kill了

  即便查看history命令,也只有一個top命令,徹底看不出什麼,因此使用top命令也要當心啊,捕獲SIGTERM也是一個不錯的主意。

 

  固然,作好權限控制,就能減小進程被意外Kill,特別是在線上服務器,適當的監控也是必要的。

  那麼當進程消失的時候,能夠按照下列步驟排查

  (1)看日誌

  (2)查看有沒有coredump,查看ulimit -c

  (3)看系統內存使用量,用dmesg看是否是OOM

  (4)看last與history,crontab

  (5)也許一切都在運維的掌控之中 

    

  

references

Taming the OOM killer

who-killed-my-process-and-why

Who sends a SIGKILL to my process mysteriously on ubuntu server

相關文章
相關標籤/搜索