블록 장치는 버퍼링을 제공합니다.. 이는 write()
커널이 장치에 데이터를 쓰기 전에 블록 장치에서 성공이 반환될 수 있음을 의미합니다. 프로그램은 를 호출하여 모든 버퍼링된 쓰기를 기다릴 수 있습니다 fsync()
.
dd
(또는 )을 사용하여 cat
파일 시스템 이미지를 장치에 기록했습니다. 이러한 명령은 기본적으로 호출되지 않습니다 fsync()
.
mount
다음으로 블록 장치에 파일 시스템으로 쓰고 싶다고 가정해 보겠습니다 .
내 생각에 가장 안전한 방법은 sync
설치하기 전에 이 명령을 사용하는 것입니다. 그런데 블록 장치를 동기화하지 않으면 어떻게 되나요? 파일 시스템이 아직 장치에 기록되지 않은 일부 블록을 읽으려고 할 가능성이 있습니까? 그렇다면 파일 시스템 이미지의 올바른 데이터 대신 장치의 이전 내용을 읽을 수 있습니까?
나의 주요 관심은 Linux 동작입니다. (StackExchange는 나에게 특정 질문을 하도록 권장했습니다. 그러나 대체 또는 역사적 조치에 찬성표를 줄 수도 있습니다 :-).
답변1
프로그램이 블록 장치 파일을 닫으면 Linux는 관련 캐시를 플러시하여 프로그램을 기다리게 합니다. 하지만 이것은 마지막 항목에만 적용됩니다 close()
. 다른 장치가 여전히 블록 장치를 열어둔 경우에는 이런 일이 발생하지 않습니다. 다음과 같은 경우 포함모든 파티션동일한 장비가 여전히 열려 있습니다.
따라서 일반적으로 어떤 방식으로든 장치를 동기화하는 것이 좋습니다.
안전을 위해 장치를 동기화하는 방법은 dd
options 과 함께 명령을 실행하는 것 입니다 conv=fsync
. 이것이 없으면 커널은 쓰기 오류를 반환하지 않습니다. 따라서 커널 로그( )를 보면 dmesg
오류만 알 수 있습니다.
캐시된 모든 쓰기를 기다리는 것 외에도 마지막 쓰기는 close()
모든 캐시를 삭제합니다( kill_bdev()
). 나는 명령의 출력을 관찰하여 이것을 직접 확인했습니다 free
.
linux-4.20/fs/block_dev.c:1778
static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
{
struct gendisk *disk = bdev->bd_disk;
struct block_device *victim = NULL;
mutex_lock_nested(&bdev->bd_mutex, for_part);
if (for_part)
bdev->bd_part_count--;
if (!--bdev->bd_openers) {
WARN_ON_ONCE(bdev->bd_holders);
sync_blockdev(bdev);
kill_bdev(bdev);
C 코드에 익숙하지 않은 경우 위의 마지막 블록은 다음과 같습니다.
bdev->bd_openers = bdev->bd_openers - 1;
if (bdev->bd_openers == 0) {
WARN_ON_ONCE(bdev->bd_holders);
sync_blockdev(bdev);
kill_bdev(bdev);
답변2
시스템 충돌을 적절하게 처리하려면 sync
/ 가 필요합니다 .fsync
커널은 일관성을 제공합니다. 모든 쓰기는 디스크로 플러시될 때까지 더티 버퍼에 보관되며 모든 읽기는 이러한 더티 버퍼에서 먼저 승인됩니다.
그러나 동기화 전에 전원이 끊기면 커널 버퍼가 사라지고 dd(1)의 일부가 유지되지 않습니다. 이것은 어색할 수 있습니다.
충돌 외에도 커널이 모든 것을 투명하게 처리하기 때문에 기본적으로 버퍼링을 잊어버릴 수 있습니다.