나는 dd 명령을 사용하여 블록 장치(파티션된 블록 장치가 아님)의 단일 바이트를 변경합니다. 예를 들어 /dev/nvme0n1
특정 위치(일반 파일로 관리되지 않음)에 있습니다.
dd of=${DEV:?DEV} seek=${POS:?POS} bs=1 count=1 oflag=seek_bytes conv=notrunc status=none
명령에 문제가 있습니다 sync
. 일부 컴퓨터에서는 명령이 멈추거나 완료하는 데 오랜 시간이 걸립니다.
이 명령에는 모든 파일의 캐싱이 포함되어 있는 것으로 보입니다 sync
. 일부 일관성 없는 커널 관리로 인해 속도가 느려지거나 심지어 중단될 수도 있습니다. 특히 호스트에서 여러 개의 대규모 VM이 실행 중인 경우 동기화가 매우 느려지고 때로는 30분이 소요됩니다.
그런 다음 명령을 직접 호출하면 안 되고 sync
, dd에게 다음과 같이 관련된 부분만 동기화하도록 지시해야 한다고 생각하기 시작했습니다 oflag=sync
.
dd of=${DEV:?DEV} seek=${POS:?POS} bs=1 count=1 oflag=sync,seek_bytes conv=notrunc status=none
oflag=direct, oflag=sync, conv=fsync의 차이가 명확하지 않아서 dd 소스를 뒤져보니
- oflag=sync는 O_SYNC 플래그를 사용하여 출력 파일을 열게 하며 각 쓰기 시스템 호출은 자동으로 fsync(fd)를 유발합니다.
- conv=fsync는 쓰기마다 추가 fsync 시스템 호출을 발생시킵니다.
- oflag=direct에서는 블록 크기에 512 등을 곱해야 합니다. 제 경우에는 1바이트에 불과했고 dd는 플래그를 끄고 conv=fsync로 변경했습니다.
모든 것이 괜찮아 보이지만 한 가지는 확실하지 않습니다.
출력 파일에 /dev/nvme0n1
Linux 캐시 파일이 많이 있는 경우 dd 명령이 출력 파일을 트리거하여 결국 모든 파일을 동기화합니까? (실제로는 1바이트만 장치에 동기화하고 나머지는 동기화하고 싶지 않습니다.)
커널 소스 코드를 확인하고 write(O_SYNC 플래그가 있는 fd)가 궁극적으로 [fs/sync.c#L180)(https://github.com/torvalds/linux/blob/16a8829130ca22666ac6236178a6233208d425c3/fs/sync.c # L180) (적어도 이것이 fsync 시스템 호출이 궁극적으로 호출하는 것입니다)
int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)
{
struct inode *inode = file->f_mapping->host;
if (!file->f_op->fsync)
return -EINVAL;
if (!datasync && (inode->i_state & I_DIRTY_TIME))
mark_inode_dirty_sync(inode);
return file->f_op->fsync(file, start, end, datasync);
}
그런데 난 막혔어
file->f_op->fsync(file, start, end, datasync)
파일 시스템 드라이버가 fsync를 어떻게 처리하는지 잘 모르겠습니다. 다른 fd로 인한 모든 캐싱이 포함되는지 여부는 분명하지 않습니다.
계속해서 커널 소스 코드를 확인하고 나중에 수정 사항을 추가하겠습니다.
편집: 나는 이것이 vfs_fsync_range
시스템 호출이 호출하는 것이라고 거의 확신합니다 write
.
스택은 이렇게 생겼어요
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
size_t, count)
{
return ksys_write(fd, buf, count);
}
ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count)
{
...
ret = vfs_write(f.file, buf, count, ppos);
}
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
...
if (file->f_op->write)
ret = file->f_op->write(file, buf, count, pos);
else if (file->f_op->write_iter)
ret = new_sync_write(file, buf, count, pos);
...
}
static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
...
ret = __generic_file_write_iter(iocb, from);
if (ret > 0)
ret = generic_write_sync(iocb, ret);
...
}
static inline ssize_t generic_write_sync(struct kiocb *iocb, ssize_t count)
{
if (iocb_is_dsync(iocb)) {
int ret = vfs_fsync_range(iocb->ki_filp,
iocb->ki_pos - count, iocb->ki_pos - 1,
(iocb->ki_flags & IOCB_SYNC) ? 0 : 1);
if (ret)
return ret;
}
return count;
}
계속될…
static int blkdev_fsync(struct file *filp, loff_t start, loff_t end,
int datasync)
{
struct block_device *bdev = filp->private_data;
int error;
error = file_write_and_wait_range(filp, start, end);
if (error)
return error;
/*
* There is no need to serialise calls to blkdev_issue_flush with
* i_mutex and doing so causes performance issues with concurrent
* O_SYNC writers to a block device.
*/
error = blkdev_issue_flush(bdev);
if (error == -EOPNOTSUPP)
error = 0;
return error;
}
위에서 수행한 동기화 작업이어야 합니다 blkdev_fsync
. 이 기능 관점에서는 분석하기가 어려워집니다. 일부 커널 개발자가 나를 도울 수 있기를 바랍니다.
위의 함수는 함수를 추가로 호출합니다. mm/filemap.c
그리고블록/blk-flush.c, 도움이 되었기를 바랍니다.
테스트를 해보고 싶지만 테스트를 통해 자신감이 생기지 않습니다. 그래서 제가 이 질문을 드리는 이유입니다.
테스트했지만 sync
명령 자체도 빠르게 완료되므로 명령 dd oflag=sync
보다 if가 sync
안전하다고 말할 수는 없습니다.
편집하다:
dd oflag=sync
나는 그것이 명령하는 것보다 더 안전하고 빠르다는 것을 확인했으며 sync
, 이 질문에 대한 대답은 '예'라고 믿습니다.
write(fd with O_SYNC)는 해당 fd에 대한 데이터만 플러시하고 동일한 파일에 대한 다른 fd로 인해 발생한 모든 캐시는 플러시하지 않습니까?
예.
테스트는 다음과 같습니다.
- 무작위 데이터로 대용량 파일을 반복적으로 생성
for i in {1..10}; do echo $i; dd if=/dev/random of=tmp$i.dd count=$((10*1024*1024*1024/512)); done
- 즉,
sync
확인을 위해 달리는 것은 거기에 매달린 것처럼 느릴 것입니다. 동기화 명령을 중단합니다. - 테스트 파일을 생성하고 물리적 LBA를 얻습니다.
echo test > z
DEV=$(df . | grep /dev |awk '{print $1}')
BIG_LBA=$(sudo debugfs -R "stat $PWD/z" $DEV | grep -F '(0)' | awk -F: '{print $2}')
- 즉, dd 명령을 실행하여 매우 빠른지 확인합니다.
dd of=${DEV:?DEV} seek=$((BIG_LBA*8*512)) bs=1 count=1 oflag=sync,seek_bytes conv=notrunc status=none <<<"x"
하지만 나는 여전히 누군가 소스 코드에서 내가 답을 확인할 수 있는 위치를 알려줄 수 있기를 바라고 있습니다.
답변1
/dev/nvme0n1
장치의 블록에 직접 마운트하고 쓰는지 확실하지 않지만 어떤 경우에도 다음 명령을 사용하여 장치에서 수행되는 I/O 작업을 추적할 수 있습니다.숨은참조,BPF 컴파일러 컬렉션. 여기 biosnoop
에는 시스템 디스크에 대한 각 I/O의 명령, 프로세스 ID, 블록 번호, 읽기 또는 쓰기 길이 및 대기 시간을 표시하는 도구가 있습니다 . Fedora에는 패키지가 있고 bcc-tools
, 다른 배포판에도 유사한 패키지가 있을 것입니다.
$ sudo /usr/share/bcc/tools/biosnoop
TIME(s) COMM PID DISK T SECTOR BYTES LAT(ms)
8.241172 dd 12525 sdc R 4000 4096 0.97
8.242739 dd 12525 sdc W 4000 4096 1.17