파이프 버퍼가 꽉 찼는 지 어떻게 알 수 있습니까?


11

하나의 프로그램에서 출력 한 Perl로 출력을 파이핑하고 있습니다. 이 과정은 때로는 며칠 동안 오래 걸리는 과정이므로 병목 현상의 위치를 ​​찾아서 열려고합니다. 스크립트가 처리 할 수있는 것보다 빠른 속도로 데이터가 스크립트로 파이프되고 있는지 알고 싶습니다. 이 경우 스크립트를 조정하려고 시도하지만 필요하지 않은 경우에는 사용하지 않습니다. 버퍼가 가득 차면 더 이상 쓰기를 방지하는 플래그 설정에 대한 이야기가 있지만 플래그가 설정되어 있는지 또는 얼마나 자주 설정되는지 확인하는 방법은 무엇입니까? 어떤 아이디어?


3
나는 글쓰기 과정 만 알 수 있다고 생각합니다.
enzotib

5
pv파이프 체인을 따라 어딘가에 사용할 수 있습니다.
amphetamachine

답변:


9

strace(Linux), dtruss(OS X), ktrace(FreeBSD), truss(Solaris) 등과 같은 시스템 호출 추적 도구를 사용하여 Perl 스크립트를 추적합니다 . 목표는 Perl 스크립트가 그 스크립트를 읽는 데 얼마나 많은 시간을 소비하는지 보는 것입니다. stdin과 다른 프로그램이 stdout에 쓰는 데 걸리는 시간

다음은 라이터를 병목 현상으로 사용하여 테스트하고 있습니다.

terminal 1$ gzip -c < /dev/urandom | cat > /dev/null

terminal 2$ ps auxw | egrep 'gzip|cat'
slamb    25311 96.0  0.0  2852  516 pts/0    R+   23:35   3:40 gzip -c
slamb    25312  0.8  0.0  2624  336 pts/0    S+   23:35   0:01 cat

terminal 2$ strace -p 25312 -s 0 -rT -e trace=read
Process 25312 attached - interrupt to quit
     0.000000 read(0, ""..., 4096) = 4096 <0.005207>
     0.005531 read(0, ""..., 4096) = 4096 <0.000051>

여기서 첫 번째 숫자는 이전 syscall이 시작된 이후의 시간이고 마지막 숫자는 syscall에 소요 된 시간입니다. 그래서 우리는 Perl로 약간의 후 처리를하여 그것을 집계 할 수 있습니다 ... [*]

terminal 2$ strace -p 25312 -s 0 -rT -e trace=read 2>&1 | perl -nle 'm{^\s*([\d.]+) read\(0, .*<([\d.]+)>} or next; $total_work += $1 - $last_wait; $total_wait += $2; $last_wait = $2; print "working for $total_work sec, waiting for $total_wait sec"; $last_wait = $2;'
working for 0 sec, waiting for 0.005592 sec
...
working for 0.305356 sec, waiting for 2.28624900000002 sec
...

terminal 2$ strace -p 25311 -s 0 -rT -e trace=write 2>&1 | perl -nle 'm{^\s*([\d.]+) write\(1, .*<([\d.]+)>} or next; $total_work += $1 - $last_wait; $total_wait += $2; $last_wait = $2; print "working for $total_work sec, waiting for $total_wait sec"; $last_wait = $2;'
...
working for 5.15862000000001 sec, waiting for 0.0555740000000007 sec
...

한 번에 양쪽을 추적하고 올바른 파일 설명 자만 추적하며 매초마다 멋진 상태 업데이트를 인쇄하는 방식으로 서로를 기다리는 SystemTap 또는 DTrace 스크립트를 더 멋지게 만들 수 있습니다.

[*]-경고 : 다른 파일 디스크립터에서 읽기 / 쓰기가 호출되는 경우 조잡한 집계가 올바르지 않습니다. 이 경우 작업 시간을 과소 평가합니다.


dtrace 버전은 실제로 매우 깔끔합니다.

terminal 1$ gzip -c < /dev/urandom | cat > /dev/null

terminal 2$ ps aux | egrep 'gzip| cat'
slamb    54189  95.8  0.0   591796    584 s006  R+   12:49AM  22:49.55 gzip -c
slamb    54190   0.4  0.0   599828    392 s006  S+   12:49AM   0:06.08 cat

terminal 2$ cat > pipe.d <<'EOF'
#!/usr/sbin/dtrace -qs

BEGIN
{
  start = timestamp;
  writer_blocked = 0;
  reader_blocked = 0;
}

tick-1s, END
{
  this->elapsed = timestamp - start;
  printf("since startup, writer blocked %3d%% of time, reader %3d%% of time\n",
         100 * writer_blocked / this->elapsed,
         100 * reader_blocked / this->elapsed);
}

syscall::write:entry
/pid == $1 && arg0 == 1/
{
  self->entry = timestamp;
}

syscall::write:return
/pid == $1 && self->entry != 0/
{
  writer_blocked += timestamp - self->entry;
  self->entry = 0;
}

syscall::read:entry
/pid == $2 && arg0 == 0/
{
  self->entry = timestamp;
}

syscall::read:return
/pid == $2 && self->entry != 0/
{
  reader_blocked += timestamp - self->entry;
  self->entry = 0;
}
EOF

terminal 2$ chmod u+x pipe.d
terminal 2$ sudo ./pipe.d 54189 54190
since startup, writer blocked   0% of time, reader  98% of time
since startup, writer blocked   0% of time, reader  99% of time
since startup, writer blocked   0% of time, reader  99% of time
since startup, writer blocked   0% of time, reader  99% of time
since startup, writer blocked   0% of time, reader  99% of time
^C
since startup, writer blocked   0% of time, reader  99% of time

그리고 SystemTap 버전 :

terminal 1$ gzip -c /dev/urandom | cat > /dev/null

terminal 2$ ps auxw | egrep 'gzip| cat'
slamb     3405  109  0.0   4356   584 pts/1    R+   02:57   0:04 gzip -c /dev/urandom
slamb     3406  0.2  0.0  10848   588 pts/1    S+   02:57   0:00 cat

terminal 2$ cat > probes.stp <<'EOF'
#!/usr/bin/env stap

global start
global writer_pid
global writes
global reader_pid
global reads

probe begin {
  start = gettimeofday_us()
  writer_pid = strtol(argv[1], 10)
  reader_pid = strtol(argv[2], 10)
}

probe timer.s(1), end {
  elapsed = gettimeofday_us() - start
  printf("since startup, writer blocked %3d%% of time, reader %3d%% of time\n",
         100 * @sum(writes) / elapsed,
         100 * @sum(reads) / elapsed)
}

probe syscall.write.return {
  if (pid() == writer_pid && $fd == 1)
    writes <<< gettimeofday_us() - @entry(gettimeofday_us())
}

probe syscall.read.return {
  if (pid() == reader_pid && $fd == 0)
    reads <<< gettimeofday_us() - @entry(gettimeofday_us())
}
EOF

terminal 2$ chmod a+x probes.stp
terminal 2$ sudo ./pipe.stp 3405 3406
since startup, writer blocked   0% of time, reader  99% of time
...

6

pv -TC파이프 라인에 명령을 삽입 할 수 있습니다 .

cmd1 | pv -TC | cmd2

pv자체 버퍼를 사용하고 -T평균적으로 1 초 간격으로 얼마나 꽉 찬지보고합니다 (기본값).

항상 100 %라면 cmd1출력보다 cmd2소비 속도가 빠릅니다 . 그렇지 않다면 다른 방법입니다. 파이프 자체는 64kB를 수용 할 수 있습니다.

의 버퍼 크기 -B를 지정하려면 참조하십시오 pv. 다음 pv과 같이 여러 가지를 사용할 수 있습니다 .

$ cmd1 | pv -cCTrbN 'cmd1 -> cmd2' | cmd2 | pv -cCTrbN 'cmd2 -> cmd3' | cmd3
cmd1 -> cmd2: 1.92GiB { 53%} [ 387MiB/s]
cmd2 -> cmd3: 1.92GiB {  0%} [ 387MiB/s]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.