Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

プロセス管理の初歩テクニック

Index

  1. 一言
  2. Terminalログアウト後もプロセスを残す
  3. プロセスの2重起動防止
  4. 簡単に不要なプロセスをkillする
  5. tmuxの薦め

一言

はてなって不思議なコミュニティーで具体的な実装や調査分析よりも便利なコマンドとかを紹介するネタが何故か重宝されるようなので、良く使うプロセス管理系コマンドの初歩テクニックを書いてみます。( コマンドの学習をより必要とする人が多く一般的な話だからでしょうか? )

Terminalログアウト後もプロセスを残す

単一の処理で長時間かかってしまうようなプログラムを実行後にログアウトするとプロセスが消えて悲惨な目に遭います。tmuxやscreenを使って再起動可能な設定をしている人は特に気にする必要はありませんが、これらを使っていない場合はnohupでログアウト後もプロセスを残しましょう。

$ vi nohuptest.sh
#!/bin/sh
sleep 1000

$ chmod +x ./nohuptest.sh

$ nohup ./nohuptest.sh &
[1] 13564
#nohup: ignoring input and appending output to `nohup.out'

$ exit

# 再度ログイン

$ ps auxww | grep nohuptest
yuta     13564  0.0  0.1 106088  1172 ?        SN   03:56   0:00 /bin/sh ./nohuptest.sh
yuta     14059  0.0  0.0 107456   948 pts/6    S+   04:00   0:00 grep nohuptest

nohupの指定を忘れてしまった場合でも途中からプロセスを残したい場合はdisownコマンドを利用すれば問題無しです。

$ ./nohuptest.sh &
[1] 16004

$ disown %1

$ exit

# 再度ログイン

$ ps auxww | grep nohuptest
yuta     16079  0.0  0.1 106088  1168 ?        SN   04:17   0:00 /bin/sh ./nohuptest.sh
yuta     16173  0.0  0.0 107456   948 pts/8    S+   04:18   0:00 grep nohuptest

プロセスの2重起動防止

バッチ処理等でプロセスが2重で起動するとデータの不整合が発生する可能性があります。それを防止するためにプロセスの2重起動を防止することを考えます。プロセスの2重起動防止はプロセス名で判断するかLockファイルで実行中フラグを管理する2種類の方法が良く使われます。Lockファイルだとプロセスが途中で落ちた時に削除されない事がありずっと実行中と判断されてしまうので、プロセス名で判断する方がより良いように思います。一応二つともやり方を書いておきます。プロセスが存在するかどうかはpgrep、Lockファイル削除trapコマンドでシグナルを受け取って行います。下のスクリプトでは10秒以内に再度処理を実行しようとすると"Process is Running"と表示されると思います。

#!/bin/sh
if [ $$ != `pgrep -fo $0` ]; then
   echo "Process is Running";
   exit 1;
fi
echo "Process is Started"
sleep 10;
echo "Process is Done";
#!/bin/sh
lockfile="$0.lock"
trap 'echo "trapped."; rm -f ${lockfile}; exit 1' 1 2 3 15

if [ -e $lockfile ];then
   echo "Process is Running"
fi

touch $lockfile;
echo "Process is Started"
sleep 10
rm $lockfile
echo "Process is Done"

trapはシグナルを受け取るコマンドです。一般的には1 2 3 15のkillシグナルを受けるように設定するようです。番号の意味ですが1は再起動2はCTR + Cの割り込み3はCTR + \の中断15はプロセスの終了を表します。trapコマンドで気をつけたいのが9の強制終了を受け取る事が出来ません(9を指定しても)。よってkill -9で指定されるとtrap出来ずlockファイルが残ってしまうので注意が必要です。上のshellscriptをtraptest.shとし、killの操作を実行してみます。

$ ./traptest.sh

#別のterminal windowからpgrepで検知してkill
$ pgrep -f "traptest" | xargs kill

# trapされている事が分かる。
Process is Started
trapped.
$ ./traptest.sh

#別のterminal windowからpgrepで検知してkill -9
$ pgrep -f "traptest" | xargs kill -9

# trapされていない事が分かる。
Process is Started
zsh: killed     ./traptest.sh

# traptest.sh.lockが残ってしまう。
$ ls -al
-rwxr-xr-x.  1 yuta yuta  237  819 02:06 2013 traptest.sh
-rw-r--r--.  1 yuta yuta    0  819 02:07 2013 traptest.sh.lock

簡単に不要なプロセスをkillする

psという起動中のプロセスを表示する代表的なコマンドがありますが、killしたい場合は少々使いづらかったりします。psには自身のコマンドがどうしても含まれてしまうのでそれをgrep -vで除外する必要があります。例えばhttpdプロセスを全部削除したい場合は以下のようにpsとkillをパイプします。

$ ps auxww | grep httpd
ps auxww | grep httpd
root      2149  0.0  0.8 264888  8468 ?        Ss   Aug18   0:03 /usr/sbin/httpd
apache    2158  0.0  0.5 264888  5280 ?        S    Aug18   0:00 /usr/sbin/httpd
apache    2160  0.0  0.5 264888  5280 ?        S    Aug18   0:00 /usr/sbin/httpd
apache    2161  0.0  0.5 264888  5280 ?        S    Aug18   0:00 /usr/sbin/httpd
apache    2162  0.0  0.5 264888  5280 ?        S    Aug18   0:00 /usr/sbin/httpd
apache    2163  0.0  0.5 264888  5280 ?        S    Aug18   0:00 /usr/sbin/httpd
apache    2165  0.0  0.5 264888  5280 ?        S    Aug18   0:00 /usr/sbin/httpd
apache    2166  0.0  0.5 264888  5280 ?        S    Aug18   0:00 /usr/sbin/httpd
apache    2167  0.0  0.5 264888  5280 ?        S    Aug18   0:00 /usr/sbin/httpd
yuta      4741  0.0  0.0 107460   948 pts/0    S+   02:14   0:00 grep httpd

$ ps auxww | grep httpd | grep -v grep | awk '{print $2}' | xargs sudo kill

$ ps auxww | grep httpd
yuta      4810  0.0  0.0 107456   940 pts/0    S+   02:17   0:00 grep httpd

grep -vとawkがかなり冗長的な命令のように思えてしまいます。そのように感じた人は上でも使ったpgrepでプロセス名から直接killしても良いと思います。

$ ps auxww | grep httpd 
root      4845  0.9  0.9 264888  9276 ?        Ss   02:20   0:00 /usr/sbin/httpd
apache    4848  0.0  0.5 264888  5292 ?        S    02:20   0:00 /usr/sbin/httpd
apache    4849  0.0  0.5 264888  5292 ?        S    02:20   0:00 /usr/sbin/httpd
apache    4850  0.0  0.5 264888  5292 ?        S    02:20   0:00 /usr/sbin/httpd
apache    4851  0.0  0.5 264888  5292 ?        S    02:20   0:00 /usr/sbin/httpd
apache    4852  0.0  0.5 264888  5292 ?        S    02:20   0:00 /usr/sbin/httpd
apache    4853  0.0  0.5 264888  5292 ?        S    02:20   0:00 /usr/sbin/httpd
apache    4854  0.0  0.5 264888  5292 ?        S    02:20   0:00 /usr/sbin/httpd
apache    4855  0.0  0.5 264888  5292 ?        S    02:20   0:00 /usr/sbin/httpd
yuta      4859  0.0  0.0 107460   944 pts/0    S+   02:20   0:00 grep httpd

$ pgrep -f "httpd"
4845
4848
4849
4850
4851
4852
4853
4854
4855
# 4859の自身のgrepは表示されない

$ pgrep -f "httpd" | xargs sudo kill

$ ps auxww | grep httpd             
yuta      4880  0.0  0.0 107456   944 pts/0    S+   02:23   0:00 grep httpd

さらにxargsさえも使う事が面倒だという男気溢れる人は次のようにpkillもしくはkillallで一発でお別れを告げても良いと思います。

$ ps auxww | grep httpd
root      4902  0.1  0.9 264888  9272 ?        Ss   02:25   0:00 /usr/sbin/httpd
apache    4905  0.0  0.5 264888  5288 ?        S    02:25   0:00 /usr/sbin/httpd
apache    4906  0.0  0.5 264888  5288 ?        S    02:25   0:00 /usr/sbin/httpd
apache    4907  0.0  0.5 264888  5288 ?        S    02:25   0:00 /usr/sbin/httpd
apache    4908  0.0  0.5 264888  5288 ?        S    02:25   0:00 /usr/sbin/httpd
apache    4909  0.0  0.5 264888  5288 ?        S    02:25   0:00 /usr/sbin/httpd
apache    4910  0.0  0.5 264888  5288 ?        S    02:25   0:00 /usr/sbin/httpd
apache    4911  0.0  0.5 264888  5288 ?        S    02:25   0:00 /usr/sbin/httpd
apache    4912  0.0  0.5 264888  5288 ?        S    02:25   0:00 /usr/sbin/httpd
yuta      4922  0.0  0.0 107460   948 pts/0    S+   02:26   0:00 grep httpd

$ sudo pkill -f "httpd"

$ ps auxww | grep httpd
yuta      4929  0.0  0.0 107456   940 pts/0    S+   02:27   0:00 grep httpd

$ sudo /etc/init.d/httpd restart
$ ps auxww | grep httpd
root      5045  0.1  0.9 264888  9236 ?        Ss   02:37   0:00 /usr/sbin/httpd
apache    5048  0.0  0.5 264888  5296 ?        S    02:37   0:00 /usr/sbin/httpd
apache    5049  0.0  0.5 264888  5296 ?        S    02:37   0:00 /usr/sbin/httpd
apache    5050  0.0  0.5 264888  5296 ?        S    02:37   0:00 /usr/sbin/httpd
apache    5051  0.0  0.5 264888  5296 ?        S    02:37   0:00 /usr/sbin/httpd
apache    5052  0.0  0.5 264888  5296 ?        S    02:37   0:00 /usr/sbin/httpd
apache    5053  0.0  0.5 264888  5296 ?        S    02:37   0:00 /usr/sbin/httpd
apache    5054  0.0  0.5 264888  5296 ?        S    02:37   0:00 /usr/sbin/httpd
apache    5055  0.0  0.5 264888  5296 ?        S    02:37   0:00 /usr/sbin/httpd
yuta      5062  0.0  0.0 107460   944 pts/0    S+   02:38   0:00 grep httpd

$ sudo killall "httpd"

$ ps auxww | grep httpd
yuta      5067  0.0  0.0 107456   944 pts/0    S+   02:38   0:00 grep httpd

tmuxの薦め

あまりプロセスと関係ありませんが、screenを使っている人は更に機能豊富なtmuxを乗り換えると良いと思います。1つのプロセスでworking spaceとなるwindowを複数立てたりpaneと呼ばれる単位の子windowへの分割もできます。またtmuxユーザーはちゃんとデタッチすれば、上で紹介したnohupやdisownを意識する必要が無いです。tmuxはyumで簡単にinstallも出来ますし、keybindもscreenとさほど変わらないように設定できます。設定ファイルは以下が参考になると思います。zsh, tmux, vimで構築する快適なCUI環境 2/3 「tmux」編 - ナレッジエース はてなブックマーク - zsh, tmux, vimで構築する快適なCUI環境 2/3 「tmux」編 - ナレッジエース.tmux.confを設定し、tmuxコマンドで起動します。これほど便利なtmuxをまだ使わずにscreenにしがみつく開発者が同環境に存在した場合、今回覚えたpgrepやkillallでscreenプロセスを一匹残らず駆逐するのも良いかと思います。(冗談です)

$ sudo yum install tmux -y
$ vi ~/.tmux.conf

#新しいウィンドウのベース番号
set-option -g base-index 1
#全てのベルを無視
set-option -g bell-action none
#各セッションで保持するバッファ数
set-option -g buffer-limit 20
#256色端末を使用
set-option -g default-terminal "xterm"
set-option -g default-command /usr/local/bin/zsh
set-option -g default-shell /usr/local/bin/zsh

#ウィンドウ履歴で保持される最大行数
set-option -g history-limit 5000
#Escapeキー入力時の待ち時間(0.5秒)をキャンセル
set-option -s escape-time 0
#ウィンドウを実行コマンド名で自動リネーム
set-window-option -g automatic-rename on
#スクロールモード、コピーモード、選択モードで vi のキーバインドを使う
set-window-option -g mode-keys vi
#ウィンドウで動作があるとステータスラインでハイライト
set-window-option -g monitor-activity on
#UTF-8 シーケンスが現れることに備える
set-window-option -g utf8 on
 
#set-option -g mouse-resize-pane on
#set-option -g mouse-select-pane on
 
# prefix + r で設定ファイルを再読み込み
set-option -g prefix C-g
unbind r
bind r source-file ~/.tmux.conf
 
# キーバインド
# 各種ペインの移動
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# 各種ペインのリサイズ
bind H resize-pane -L 5
bind J resize-pane -D 5
bind K resize-pane -U 5
bind L resize-pane -R 5

#マウススクロールバック
set-window-option -g mode-mouse on

#status line
set -g status-interval 5
set -g status-fg cyan
set -g status-bg black
set -g status-left-length 40 
set -g status-left '#[fg=white,bg=blue]#H#[fg=white,bg=black]:#[fg=white,bg=black][#S#[fg=white,bg=black]][#[fg=white,bg=black]'
set -g status-right '#[fg=white,bg=cyan][CPU=#(cpu.sh) MEM=#(mem.sh)]#[fg=black,bg=green][%Y/%m/%d(%a)%H:%M]#[default]'

setw -g window-status-current-fg cyan
setw -g window-status-current-bg black
setw -g window-status-current-attr bold#,underscore
# 以上が設定ファイル

$ cat mem.sh cpu.sh 
#!/bin/sh
free | grep "^Mem:" | awk '{printf ("%3d%\n", (($3 / $2) * 100) + 0.5)}'
#!/bin/sh
top -bn1 | grep "^Cpu(s)" | sed "s/.*, *\([0-9.]*\)%id.*/\1/" | awk '{printf ("%3d%\n", 100 - $1 + 0.5)}'

# tmuxの起動
$ tmux 

# screenを使っている奴らを駆逐する。
$ sudo killall "screen"