스크립트를 통해 가상 머신 이미지 파일을 압축하려고 하는데 파일에 액세스하고 있지 않은지 확인하고 싶습니다. 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
이는 가상 머신 디스크 이미지 파일/장치를 열어두는 프로세스가 아닙니다. 이는 바이너리 중 하나입니다 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