
1TB 파일이 있습니다. 나는 바이트 12345678901부터 바이트 19876543212까지 읽고 이를 100MB RAM이 있는 시스템의 표준 출력에 넣고 싶습니다.
나는 이것을 수행하는 Perl 스크립트를 쉽게 작성할 수 있습니다. sysread는 700MB/s를 제공하지만(양호함) syswrite는 30MB/s만 제공합니다. 나는 좀 더 효율적인 것을 원합니다. 모든 Unix 시스템에 설치되고 1GB/s 정도의 속도를 제공할 수 있는 것이 바람직합니다.
내 첫 번째 아이디어는 다음과 같습니다.
dd if=1tb skip=12345678901 bs=1 count=$((19876543212-12345678901))
그러나 그것은 효율적이지 않습니다.
편집하다:
syswrite를 어떻게 잘못 측정했는지 전혀 모르겠습니다. 이는 3.5GB/s를 제공합니다.
perl -e 'sysseek(STDIN,shift,0) || die; $left = shift; \
while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){ \
$left -= $read; syswrite(STDOUT,$buf);
}' 12345678901 $((19876543212-12345678901)) < bigfile
그리고 yes | dd bs=1024k count=10 | wc
악몽을 피합니다.
답변1
블록 크기가 작기 때문에 속도가 느립니다. 최근 GNU 사용 dd
(코어틸스 v8.16 +), 가장 간단한 방법은 skip_bytes
및 count_bytes
옵션을 사용하는 것입니다.
in_file=1tb
start=12345678901
end=19876543212
block_size=4096
copy_size=$(( $end - $start ))
dd if="$in_file" iflag=skip_bytes,count_bytes,fullblock bs="$block_size" \
skip="$start" count="$copy_size"
업데이트
fullblock
위에 따라 추가된 옵션@Gilles 답변. 처음에는 을 암시하는 것일지도 모른다고 생각했는데 count_bytes
, 그렇지 않았습니다.
언급된 문제는 아래의 잠재적인 문제입니다. dd
어떤 이유로든 읽기/쓰기 호출이 중단되면 데이터가 손실됩니다. 대부분의 경우에는 그럴 가능성이 없습니다(파이프가 아닌 파일에서 읽기 때문에 확률이 다소 줄어듭니다).
및 옵션 dd
없이 a를 사용하는 것이 더 어렵습니다.skip_bytes
count_bytes
in_file=1tb
start=12345678901
end=19876543212
block_size=4096
copy_full_size=$(( $end - $start ))
copy1_size=$(( $block_size - ($start % $block_size) ))
copy2_start=$(( $start + $copy1_size ))
copy2_skip=$(( $copy2_start / $block_size ))
copy2_blocks=$(( ($end - $copy2_start) / $block_size ))
copy3_start=$(( ($copy2_skip + $copy2_blocks) * $block_size ))
copy3_size=$(( $end - $copy3_start ))
{
dd if="$in_file" bs=1 skip="$start" count="$copy1_size"
dd if="$in_file" bs="$block_size" skip="$copy2_skip" count="$copy2_blocks"
dd if="$in_file" bs=1 skip="$copy3_start" count="$copy3_size"
}
다양한 블록 크기로 실험할 수도 있지만 그 효과는 그다지 크지 않습니다. 보다 -dd에 대한 bs 매개변수의 최적 값을 결정하는 방법이 있습니까?
답변2
bs=1
dd
한 번에 한 바이트씩 읽고 쓰라고 지시합니다 . 각 read
및 호출 마다 오버헤드가 발생 write
하므로 속도가 느려집니다. 적절한 성능을 위해서는 더 큰 블록 크기를 사용하십시오.
적어도 Linux에서는 전체 파일을 복사할 때 다음과 같은 사실을 발견했습니다.cp
그리고 cat
보다 빠르다dd
, 큰 블록 크기를 지정하더라도 마찬가지입니다.
파일의 일부만 복사 tail
하려면 head
. 이를 위해서는 head -c
지정된 바이트 수를 복사해야 하는 GNU coreutils 또는 기타 구현이 필요합니다 ( tail -c
POSIX에는 있지만 head -c
그렇지 않음). Linux에 대한 빠른 벤치마크에 dd
따르면 아마도 파이프 때문에 이것이 보다 느린 것으로 나타났습니다 .
tail -c $((2345678901+1)) | head -c $((19876543212-2345678901))
문제는 dd
그게신뢰할 수 없습니다. 부분 데이터를 복사할 수 있습니다.. 내가 아는 한, dd
일반 파일을 읽고 쓸 때는 안전합니다.dd는 언제 데이터 복사에 적합합니까? (또는 read() 및 write()가 부분적인 경우)- 하지만신호에 의해 방해받지 않는 한. GNU coreutils를 사용하면 플래그를 사용할 수 있지만 fullblock
이식 가능하지 않습니다.
또 다른 문제는 dd
건너뛴 바이트 수와 전송된 바이트 수 모두 블록 크기의 배수여야 하기 때문에 작동하는 블록 수를 찾기가 어려울 수 있다는 것입니다. 여러 호출을 사용할 수 있습니다 dd
. 하나는 첫 번째 부분 블록을 복사하고, 하나는 정렬된 블록의 대량을 복사하고, 다른 하나는 마지막 부분 블록을 복사합니다. — 참조그레이엄의 답변쉘 조각의 경우. 그러나 스크립트를 실행할 때 플래그를 사용하지 않는 한 모든 데이터가 복사되도록 fullblock
기도해야 한다는 점을 잊지 마십시오 . 복사본이 부분적인 경우 0이 아닌 상태를 반환하므로 오류를 쉽게 감지할 수 있지만 실제로 복구할 수 있는 방법은 없습니다.dd
dd
POSIX는 쉘 수준에서 제공하는 것보다 더 좋은 것은 없습니다. 내 조언은 작은 특수 목적의 C 프로그램을 작성하라는 것입니다(정확히 무엇을 구현하는지에 따라 dd_done_right
또는 tail_head
또는 이라고 부를 수 있습니다 mini-busybox
).
답변3
와 함께 dd
:
dd if=1tb skip=12345678901 count=$((19876543212-12345678901)) bs=1M iflags=skip_bytes,count_bytes
또는 다음을 사용하여 losetup
:
losetup --find --show --offset 12345678901 --sizelimit $((19876543212-12345678901))
그리고 dd
, cat
, ... 루프 장치.
답변4
이렇게 하면 됩니다.
i=$(((t=19876543212)-(h=12345678901)))
{ dd count=0 skip=1 bs="$h"
dd count="$((i/(b=64*1024)-1))" bs="$b"
dd count=1 bs="$((i%b))"
} <infile >outfile
이것이 실제로 필요한 전부입니다. 더 이상 필요하지 않습니다. 우선 일반 파일 입력을 실질적으로 즉각적으로 처리 dd count=0 skip=1 bs=$block_size1
합니다 . lseek()
가능성은 없다누락된 데이터또는 그것에 대해 다른 거짓 정보가 무엇이든 원하는 시작 위치를 직접 찾을 수 있습니다. 파일 설명자는 셸이 소유하고 는 dd
단순히 이를 상속하기 때문에 커서 위치에 영향을 미치므로 단계적으로 수행할 수 있습니다. 정말 간단합니다. .보다 작업에 더 적합한 표준 도구는 없습니다 dd
.
이는 종종 이상적인 64k 블록 크기를 사용합니다. 대중적인 믿음과는 달리, 블록 크기가 크다고 해서 작업 속도가 빨라지는 것은 아닙니다 dd
. 반면에 작은 버퍼도 좋지 않습니다. dd
데이터를 메모리에 복사하고 다시 꺼낼 때까지 기다릴 필요가 없을 뿐만 아니라 시스템 호출을 기다릴 필요도 없도록 시스템 호출에서 시간을 동기화해야 합니다. 따라서 다음 작업 read()
이 마지막을 기다릴 필요가 없을 만큼 충분한 시간이 걸리기를 원 하지만, 필요한 것보다 더 큰 크기로 버퍼링하는 데 너무 많은 시간이 걸리지는 않습니다.
따라서 첫 번째는 dd
시작 위치로 건너뜁니다. 걸리는 시간영시간. 그 시점에서 원하는 다른 프로그램을 호출하여 표준 입력을 읽을 수 있으며 원하는 바이트 오프셋에서 직접 읽기 시작합니다. 다른 사람에게 전화해서 dd
읽어요((interval / blocksize) -1)
표준 출력으로 블록을 계산합니다.
마지막으로 필요한 것은 모듈러스를 복사하는 것입니다.(만약에 어떠한)이전 분할 작업의 그리고 그게 다입니다.
그런데 사람들이 증거도 없이 얼굴로 사실을 진술한다면 믿지 마세요. 네, dd
짧게 읽어도 가능합니다(건강한 블록 장치에서 읽을 때는 그런 일이 불가능하지만 이름이 그렇습니다). 이러한 작업은 dd
블록 장치가 아닌 다른 곳에서 읽은 스트림을 올바르게 버퍼링하지 않는 경우에만 가능합니다 . 예를 들어:
cat data | dd bs="$num" ### incorrect
cat data | dd ibs="$PIPE_MAX" obs="$buf_size" ### correct
두 경우 모두 dd
사본모두데이터의. 첫 번째 경우에는 가능합니다.(그러나 가능성은 낮음 cat
)복사 되는 출력 블록 중 일부는 지정되었기 dd
때문에 "$num" 바이트와 비트가 같습니다.dd
오직명령줄에서 버퍼가 특별히 요청될 때 무엇이든 버퍼링합니다. bs=
을 나타냅니다최고블록 크기 때문에목적of는 dd
실시간 I/O입니다.
두 번째 예에서는 출력 블록 크기를 명시적으로 지정하고 dd
쓰기가 완료될 때까지 읽기를 버퍼링합니다. 이는 입력 블록을 기반으로 하는 것에 영향을 미치지 않지만 count=
이를 위해서는 다른 dd
. 그렇지 않은 경우 귀하에게 제공된 잘못된 정보는 무시되어야 합니다.