1TB 파일이 있습니다. 바이트 12345678901에서 바이트 19876543212로 읽고 100MB RAM이있는 컴퓨터의 표준 출력에 저장하고 싶습니다.
이를 수행하는 펄 스크립트를 쉽게 작성할 수 있습니다. sysread는 700MB / s (정상)를 제공하지만 syswrite는 30MB / s 만 제공합니다. 모든 유닉스 시스템에 설치되어 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
악몽을 피하십시오 .
답변
블록 크기가 작기 때문에 속도가 느립니다. 최신 GNU dd
( coreutils 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 answer에 따라 추가되었습니다 . 처음에는에 의해 암시 될 수 있다고 생각 count_bytes
했지만 이것은 사실이 아닙니다.
언급 된 문제는 아래의 잠재적 인 문제 dd
입니다. 어떤 이유로 든 읽기 / 쓰기 호출이 중단되면 데이터가 손실됩니다. 이것은 대부분의 경우에는 가능성이 낮습니다 (파이프가 아닌 파일에서 읽으므로 이상한 점이 줄어 듭니다).
및 옵션 dd
없이는 사용하기 가 더 어렵습니다.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=1
dd
한 번에 한 바이트 씩 읽고 쓰도록 지시 합니다. 각각 read
과 write
호출에 대한 오버 헤드가 있으므로 속도가 느려집니다. 적절한 성능을 위해 더 큰 블록 크기를 사용하십시오.
당신은 전체 파일을 복사 할 때, 리눅스에서 적어도, 나는 것으로 나타났습니다 cp
하고 cat
빠르게보다dd
크기가 큰 블록 크기를 지정하는 경우에도,.
파일의 일부만 복사하려면 tail
로 파이프하십시오 head
. 이를 위해서는 GNU coreutils 또는 head -c
지정된 수의 바이트를 복사 해야하는 다른 구현이 필요 합니다 ( tail -c
POSIX에는 있지만 head -c
그렇지 않음). 리눅스에서의 빠른 벤치 마크는 dd
아마도 파이프 때문에 이보다 느리다는 것을 보여줍니다 .
tail -c $((2345678901+1)) | head -c $((19876543212-2345678901))
문제 dd
는 신뢰할 수 없다는 것입니다 . 부분 데이터를 복사 할 수 있습니다 . 내가 아는 한, dd
일반 파일을 읽고 쓸 때 안전합니다. dd는 언제 데이터 복사에 적합합니까?를 참조하십시오 . (또는 read () 및 write () partial 인 경우) — signal에 의해 중단되지 않는 한에만 가능 합니다. GNU coreutils를 사용하면 fullblock
플래그를 사용할 수 있지만 이식성이 없습니다.
또 다른 문제점 dd
은 건너 뛴 바이트 수와 전송 된 바이트 수가 블록 크기의 배수 여야하기 때문에 작동하는 블록 수를 찾기가 어렵다는 것입니다. 여러 번의 호출을 사용할 수 있습니다 dd
. 하나는 첫 번째 부분 블록을 복사하고, 하나는 정렬 된 블록을 대량으로 복사하고, 다른 하나는 마지막 부분 블록을 복사합니다.- 쉘 스 니펫에 대한 Graeme의 답변 을 참조하십시오 . 그러나 스크립트를 실행할 때 fullblock
플래그를 사용하지 않는 한 dd
모든 데이터를 복사 하도록기도해야 함을 잊지 마십시오 . dd
복사본이 부분적인 경우 0이 아닌 상태를 반환하므로 오류를 쉽게 감지 할 수 있지만 실제로 복구 할 수있는 방법은 없습니다.
POSIX는 쉘 수준에서 제공하는 것이 더 좋습니다. 내 조언은 작은 특수 목적의 C 프로그램을 작성하는 것 (에 정확히 구현 무엇을 따라, 당신은 그것을 호출 할 수 있습니다 dd_done_right
또는 tail_head
이상 mini-busybox
).
답변
로 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
… 루프 장치.
답변
이것은 당신이 이것을 할 수있는 방법입니다 :
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
는 시작 위치로 건너 뜁니다. 시간 이 걸리지 않습니다 . 그 시점에서 원하는 다른 프로그램을 호출하여 stdin을 읽으면 원하는 바이트 오프셋에서 직접 읽기 시작합니다. 다른 것을 호출 하여 stdout에 카운트 블록을 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
spec’d된다 만 전혀 아무것도로 버퍼 구체적으로는 – 명령을 요청할 때 선. bs=
나타내는 최대 때문에 블록 크기를 목적 의이 dd
실시간으로 I / O이다.
두 번째 예에서는 출력 블록 크기를 명시 적으로 지정하고 dd
전체 쓰기가 완료 될 때까지 버퍼를 읽습니다. count=
입력 블록을 기반으로하는 것은 영향 을 미치지 않지만 다른 블록이 필요합니다 dd
. 그렇지 않으면 귀하에게 제공되는 모든 잘못된 정보는 무시해야합니다.