スクリプトを使用して仮想マシンイメージファイルを圧縮しようとしていますが、ファイルにアクセスしていないことを確認したいと思います。 virt-managerがイメージにアクセスする唯一のプログラムである必要があるため、実行していることを確認できますが、これを行うより良い方法があるかどうかはわかりません。また、ファイルが圧縮される準備ができるまでスクリプトを試み続けたいと思います。私も何をすべきかわかりません。
#Check if virt-manager is running
if pgrep "virt-manager" > /dev/null
then
#re-run script until success
else
gzip -k < /home/brady/.vms/windows10/hdd.img > /media/backup/vms/windows10/hdd.$(date +"%F.%T).img.gz
答え1
このlsof
コマンドは、ファイルが使用中かどうかを示します。頻繁に確認するようにwhile
ループに入れることができます。sleep
たとえば、
ウィンドウ1では、以下を実行できます。sleep 10000 > /tmp/x
ウィンドウ2で、次のスクリプトを実行します。
#!/bin/bash
FILE=/tmp/x
while [ -n "$(lsof "$FILE")" ]
do
sleep 1
done
echo "File $FILE not in use"
control-C
中断ボタンを押すと、sleep
約1秒間「ファイルが使用されていません」という応答が表示されます。
答え2
inotifyツールがインストールされているLinuxでは、次のことができます。
#! /bin/zsh -
file=${1?}
# if it's a symlink, we want the real file, readlink also tells us
# if the file is accessible
file=$(readlink -e -- "$file") || exit
# start inotifywait as a coproc so we can terminate it after we're
# done:
coproc LC_ALL=C inotifywait -me close --format . -- "$file" 2>&1
# Now wait for the "Watches established." messaged. First, that allows us
# to verify inotifywait started properly, and that also avoids the race
# condition where the last file user is gone after our fuser check but
# before the watch is in place
read <&p && read <&p && [ "$REPLY" = "Watches established." ] || exit
# Now watch CLOSE events until the file has no more user:
while fuser -s "$file" && read <&p; do continue; done
printf '"%s" is no longer used, renaming it to prevent new access\n' "$file"
kill %
ret=0
if mv -- "$file" "$file.moved-away"; then
printf 'and now compressing it\n'
pixz -t < "$file.moved-away" > "$file.xz" || ret=$?
mv -- "$file.moved-away" "$file" || ret=$? # move back
else
ret=$?
fi
exit "$ret"
を使用すると、inotifywait
ファイルのfdが閉じられるたびに通知を受け取ります。これは、ファイルを頻繁に確認する必要がなく、最後のユーザーがファイルを閉じるとすぐに圧縮を開始できることを意味します。
私のテストの結果、最初の懸念とは異なり、これはマップされたファイルにも当てはまります。この場合、イベントはCLOSE
最後に生成されず、最後にclose()
(munmap()
ファイルが完全に解放されたときに)生成されます。
fuser -s
よりも良いでしょうlsof
。fuser
標準UNIXコマンド(-s
標準オプションではありませんが、Linuxで利用可能なバージョンはそれをサポートしています)。
圧縮する前に追加のアクセスを防ぐためにファイルを移動します。
私たちはgzipよりも優れた圧縮率を提供し、より重要なのは、圧縮されたファイルがランダムにアクセスされるため(最新バージョンもマルチスレッドをサポートしますが、マルチスレッドバージョンを使用しpixz
ます。コンテンツをマウントまたは開始できます。を解放せずにnbdkitを使用するVMで)。xz
xz
または、デバイスのバックエンドとして使用されるlsof
ファイルは検出されfuser
ません。ループデバイスの場合、これを使用してファイルがこの方法で使用されていることを確認できます。たとえば、ファイルが移動された後に次のループを挿入できます。loop
mtd
losetup -j "$file"
while [ -n "$(losetup -j "$file.moved-away")" ]; do
sleep 1
done
答え3
lsof
作業に適したツールですが、基本的にすべてのPIDをチェックするので、遅くてCPUを大量に使用します。幸いなことに、作業をスピードアップする方法があります。
ただし、virt-manager
これは仮想マシンのディスクイメージファイル/デバイスを開いたままにするプロセスではありません。これはバイナリの1つですqemu
。qemu-system-x86_64
特定のプロセスのみがファイルを開く可能性があり、そのプロセスのPIDを知っているか取得できる場合は、そのプロセスをカンマ区切りリストとして提供できますlsof -p
。
pids=$(pgrep qemu-system | paste -sd,)
[ -n "$pids" ] && lsof -p "$pids" | grep -i filename
より良いことは、optionsを使用してプロセスlsof
名を指定できることです-c
。-c
プロセス名が正確に一致する必要はなく、パターン(最大長15文字)が必要です。必要に応じて、コマンドラインでこれを複数回使用できます-c
。man lsof
詳しくはlsof FAQをご覧ください。
15文字を超えて使用すると、次のエラーメッセージが表示されます。
# lsof -c qemu-system-x86_64
lsof: "-c qemu-system-x86_64" length (18) > what system provides (15)
とにかく、例えば次のようになります。
# lsof -c qemu-system | grep -i FreeBSD-10.2-RELEASE-amd64.qcow2
qemu-syst 4770 libvirt-qemu 20u REG 8,3 1837236224 403730954 /var/lib/libvirt/images/FreeBSD-10.2-RELEASE-amd64.qcow2
ループではwhile
次のようになります。
pname='qemu-system'
fname='FreeBSD-10.2-RELEASE-amd64.qcow2'
while lsof -c "$pname" | grep -qi "$fname" ; do
sleep 0.1 # don't need to sleep for as long between checks
# but if you're not impatient, leave it at 1 second
# rather than 0.1.
done
echo "$fname is not in use"
これは、仮想マシンでネイティブパーティションを使用している場合にも機能します(たとえば、fname='/dev/sda5'
上記のスクリプトスニペットで)。
ファイルベースのイメージの代わりにZFS ZVOL、LVM LVなどを使用すると、状況がより複雑になります。 lsof
実際のブロックデバイス名はシンボリックリンクを解釈した後に表示されるため、シンボリックリンクも解釈する必要があります(たとえば、readlink -f
grepを使用)。
freedos
たとえば、ZFSの場合は、プールからZVOLを呼び出しますvolumes
。
# fname=$(readlink --n f /dev/zvol/volumes/freedos)
# echo "$fname"
/dev/zd32
名前がLVのLVMの場合centos7
:
# fname=$(readlink -n -f /dev/mapper/centos7)
# echo $fname
/dev/dm-1
注:/dev/vg/centos7
交換/dev/mapper/centos7
も可能です。
find
最初はこれに答えてメソッドを作成し始めましたが、このlsof -c
方法がより良いことに気づきました。私は別のかなり速い代替案を文書化するためにここに残します。
find -lname
lsof
PIDオプションなしで実行するよりも、システムでより高速で軽量です-p
。
例えば
# sleep 10000 > /tmp/foo &
[1] 31077
# find /proc/[0-9]*/fd/ -lname '/tmp/foo'
/proc/31077/fd/1