1 flockコマンドとは?
ロックファイルを取得し、そのロックファイルを保持するコマンドを実行します。
書式は以下になります。
flock [ --shared | --timeout= seconds ] lockfile command
2 検証環境
CentOS版数は以下のとおりです。
[root@server ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core)
カーネル版数は以下のとおりです。
[root@server ~]# uname -r 3.10.0-1160.el7.x86_64
flockコマンドの版数は以下のとおりです。
[root@server ~]# flock --version flock from util-linux 2.23.2
3 オプション一覧
オプションは以下のとおりです。
[root@server ~]# flock --help Usage: flock [options] <file|directory> <command> [command args] flock [options] <file|directory> -c <command> flock [options] <file descriptor number> オプション: -s --shared get a shared lock -x --exclusive get an exclusive lock (default) -u --unlock remove a lock -n --nonblock fail rather than wait -w --timeout <secs> wait for a limited amount of time -E --conflict-exit-code <number> exit code after conflict or timeout -o --close close file descriptor before running command -c --command <command> run a single command string through the shell -h, --help display this help and exit -V, --version output version information and exit For more details see flock(1).
4 事前準備
ターミナルを2つオープンします。それぞれ、ターミナル1,ターミナル2と呼びます。
ターミナル1で実行します。
テスト用のファイルを作成します。
[root@server ~]# echo 12345 > aa [root@server ~]# cat aa 12345
ロックファイルを作成します。
[root@server ~]# touch /tmp/lock
5 排他ロックの使い方(-x)
ターミナル1で実行します。
排他モードでviコマンドを実行します。viが実行され、aaの中身が表示されます。
[root@server ~]# flock -x /tmp/lock vi aa
ターミナル2で実行します。
排他モードでcatコマンドを実行します。viコマンドがロックファイルを保持しているので、catコマンドがブロックします。
[root@server ~]# flock -x /tmp/lock cat aa
Crtl +c 押下してflock コマンドを終了します。
[root@server ~]# flock -x /tmp/lock cat aa ^C
次に、共有モードでcatコマンドを実行します。viコマンドがロックファイルを保持しているので、catコマンドがブロックします。
[root@server ~]# flock -s /tmp/lock cat aa
Crtl +c 押下してflock コマンドを終了します。
[root@server ~]# flock -s /tmp/lock cat aa ^C
6 共有ロックの使い方(-s)
ターミナル1で実行します。
共有モードでviコマンドを実行します。viが実行され、aaの中身が表示されます。
[root@server ~]# flock -s /tmp/lock vi aa
ターミナル2で実行します。
排他モードでcatコマンドを実行します。viコマンドがロックファイルを保持しているので、catコマンドがブロックします。
[root@server ~]# flock -x /tmp/lock cat aa
Crtl +c 押下してflock コマンドを終了します。
[root@server ~]# flock -x /tmp/lock cat aa ^C
共有モードでcatコマンドを実行します。viコマンドがロックファイルを保持していますが、catコマンドを共有モードで実行しているので、ブロックしません。
[root@server ~]# flock -s /tmp/lock cat aa 12345
7 まとめ
コマンドA,B共に共有ロックを使った場合だけ、コマンドがブロックしないことがわかります。
コマンドB | コマンドB | ||
共有ロック(-s) | 排他ロック(-x) | ||
コマンドA | 共有ロック(-s) | ○ | × |
コマンドA | 排他ロック(-x) | × | × |
- 〇はロックが獲得できることを意味します。
- ✖はロックが獲得できないことを意味します。
8 その他
8.1 flockコマンドが使用するシステムコールの確認
straceコマンドを使うと、flockコマンドがflockシステムコールを呼び出していることがわかります。共有モードのときは、flockシステムコールの第2引数にLOCK_SHを指定していることがわかります。なお、straceコマンドの使い方は、straceコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# strace -e trace=open,flock flock -s /tmp/lock cat aa open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 open("/tmp/lock", O_RDONLY|O_CREAT|O_NOCTTY, 0666) = 3 flock(3, LOCK_SH) = 0 12345
排他モードのときは、flockシステムコールの第2引数にLOCK_EXを指定していることがわかります。
[root@server ~]# strace -e trace=open,flock flock -x /tmp/lock cat aa open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 open("/tmp/lock", O_RDONLY|O_CREAT|O_NOCTTY, 0666) = 3 flock(3, LOCK_EX) = 0 12345
8.2 コマンド実行順序のシリアライズ化
flockコマンドを使って、コマンドの実行順序をシリアライズしてみます。
以下のTP(テストプログラム)を作成します。シェルスクリプトの作成方法は、bashスクリプトの書き方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# cat tp.sh #!/usr/bin/bash NUM_MAX=5 i=0 while [ "$i" -lt ${NUM_MAX} ] do logger "$1 (PID=$$,NUM=$i)" sleep 3 i=$((i+1)) done
ロックファイルを作成します。
[root@server ~]# touch /tmp/lock
ログを確認するため、journalctlコマンドを実行します。
[root@server ~]# journalctl -f
TPを実行します。
[root@server ~]# flock -x /tmp/lock ./tp.sh A
もう1つターミナルを開いて、TPを実行します。
[root@server ~]# flock -x /tmp/lock ./tp.sh B
もう1つターミナルを開いて、TPを実行します。
[root@server ~]# flock -x /tmp/lock ./tp.sh C
ログを確認すると、コマンドの実行順序がシリアライズされていることがわかります。
[root@server ~]# journalctl -f 2月 22 21:07:07 server root[1847]: A (PID=1846,NUM=0) 2月 22 21:07:10 server root[1850]: A (PID=1846,NUM=1) 2月 22 21:07:13 server root[1853]: A (PID=1846,NUM=2) 2月 22 21:07:16 server root[1855]: A (PID=1846,NUM=3) 2月 22 21:07:19 server root[1857]: A (PID=1846,NUM=4) 2月 22 21:07:22 server root[1860]: B (PID=1859,NUM=0) 2月 22 21:07:25 server root[1862]: B (PID=1859,NUM=1) 2月 22 21:07:28 server root[1864]: B (PID=1859,NUM=2) 2月 22 21:07:31 server root[1866]: B (PID=1859,NUM=3) 2月 22 21:07:34 server root[1868]: B (PID=1859,NUM=4) 2月 22 21:07:37 server root[1871]: C (PID=1870,NUM=0) 2月 22 21:07:40 server root[1873]: C (PID=1870,NUM=1) 2月 22 21:07:43 server root[1875]: C (PID=1870,NUM=2) 2月 22 21:07:46 server root[1877]: C (PID=1870,NUM=3) 2月 22 21:07:49 server root[1879]: C (PID=1870,NUM=4)
flockコマンドを使わないと、以下のような実行結果になります。コマンドの実行順序がシリアライズされていないことがわかります。
[root@server ~]# journalctl -f 2月 22 21:20:40 server root[1921]: A (PID=1920,NUM=0) 2月 22 21:20:42 server root[1924]: B (PID=1923,NUM=0) 2月 22 21:20:43 server root[1926]: A (PID=1920,NUM=1) 2月 22 21:20:45 server root[1929]: C (PID=1928,NUM=0) 2月 22 21:20:45 server root[1931]: B (PID=1923,NUM=1) 2月 22 21:20:46 server root[1933]: A (PID=1920,NUM=2) 2月 22 21:20:48 server root[1935]: C (PID=1928,NUM=1) 2月 22 21:20:48 server root[1937]: B (PID=1923,NUM=2) 2月 22 21:20:49 server root[1939]: A (PID=1920,NUM=3) 2月 22 21:20:51 server root[1941]: C (PID=1928,NUM=2) 2月 22 21:20:51 server root[1943]: B (PID=1923,NUM=3) 2月 22 21:20:52 server root[1945]: A (PID=1920,NUM=4) 2月 22 21:20:54 server root[1947]: C (PID=1928,NUM=3) 2月 22 21:20:54 server root[1949]: B (PID=1923,NUM=4) 2月 22 21:20:57 server root[1951]: C (PID=1928,NUM=4)
Z 参考情報
私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ