hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

flockコマンドの使い方



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技術ブログ