2013-04-16 3 views
11

기계가 패킷을 수신하여 처리하고 응답을 돌려주는 데 걸리는 시간을 확인하려고합니다.수면을 사용하면 측정 된 네트워크 대기 시간이 왜 변경됩니까?

이 기계는, I '는 서버의 전화거야, 다른 버퍼는 버퍼에 복사 수신 된 콘텐츠 (memcpy(3))를 패킷 (recv(2))를 수신하고, 피드백 패킷을 전송하는 매우 간단한 프로그램 (send(2) 실행). 서버는 NetBSD 5.1.2를 실행합니다. 내가 오류 검사 및 명확성을 위해 다른 사소한 것들을 제거

struct timespec start, end; 
for(i = 0; i < pkt_count; ++i) 
{ 
    printf("%d ", i+1); 

    clock_gettime(CLOCK_MONOTONIC, &start);   
    send(sock, send_buf, pkt_size, 0); 
    recv(sock, recv_buf, pkt_size, 0); 
    clock_gettime(CLOCK_MONOTONIC, &end);   

    //struct timespec nsleep = {.tv_sec = 0, .tv_nsec = 100000}; 
    //nanosleep(&nsleep, NULL); 

    printf("%.3f ", timespec_diff_usec(&end, &start)); 
} 

:

내 클라이언트는 왕복 시간을 번 (pkt_count)를 측정한다. 클라이언트는 Ubuntu 12.04 64 비트에서 실행됩니다. Ubuntu 커널 만 실시간 (-rt)이지만 두 프로그램 모두 실시간 우선 순위로 실행됩니다. 프로그램 간의 연결은 TCP입니다. 이것은 잘 작동하고 저에게 평균 750 마이크로 초를줍니다.

그러나 (100μs의 슬립으로) 주석 처리 된 nanosleep 호출을 활성화하면 평균 측정 값이 100μs가되어 평균 650μs가됩니다. 200μs 동안 잠을 자면 대책이 550μs로 떨어집니다. 이것은 600μs의 수면이 될 때까지 올라가며 평균 150μs가됩니다. 그런 다음, 수면을 700 μs로 올리면 측정 값은 평균 800 μs까지 올라갑니다. Wireshark로 프로그램의 조치를 확인했습니다.

나는 무슨 일이 일어나는지 알 수 없다. 클라이언트와 서버 모두에서 이미 TCP_NODELAY 소켓 옵션을 설정했지만 차이점은 없습니다. UDP를 사용했지만 차이점은 없습니다 (동일한 동작). 그래서이 행동은 Nagle 알고리즘 때문이 아니라고 생각합니다. 뭐가 될수 있었는지?

[UPDATE]는

여기서 와이어 샤크와 함께 클라이언트의 출력의 스크린 샷이다. 이제 다른 서버에서 서버를 실행했습니다. 동일한 구성의 동일한 OS (펜 드라이브의 라이브 시스템)를 사용했지만 하드웨어가 다릅니다. 이 동작이 나타나지 않아 모든 것이 예상대로 작동했습니다. 하지만 문제는 여전히 남아 있습니다 : 이전 하드웨어에서 왜 그런 일이 발생합니까?

Output Comparison

[업데이트 2 : 자세한 정보는] 내가 전에 말했듯이

, 나는 두 개의 서로 다른 서버 컴퓨터에 프로그램 내 쌍 (클라이언트/서버)를 테스트했다. 나는 두 가지 결과를 얻었다.

Comparison between two servers

제 서버 (이상한 일)는 1Gbps의 이더넷 인터페이스하는 RTD Single Board Computer이다. 두 번째 서버 (정상적인 서버)는 100Mbps 이더넷 인터페이스가있는 Diamond Single Board Computer입니다. 둘 다 SAME pendrive에서 SAME OS (NetBSD 5.1.2)를 실행합니다. 이러한 결과에서

는,이 동작은 드라이버 또는이 NIC 자체가, 그런 일이 왜 아직 ... 상상도 할 수 있지만에

+0

"wireshark를 사용하여 프로그램의 조치를 확인했습니다"라고 말하면 분명히 확인한 내용을 명확히 할 수 있습니까? 이것이 회의적으로 들리는 경우 사과 드리지만, 나노 슬립 호출이 패킷의 RTT에 실제로 영향을 주어야한다는 단순한 두뇌가 생각할 이유가 전혀 없습니다. Afterall, 그것은 당신의 recv 호출 후 온다 ... 나는 당신이 RTT를 측정하기 위해 사용하고있는 코드에 더 많은 이슈가 될 것이라고 생각한다. Wireshark가 실제로보고있는 결과를 검증한다면 내 이론은 창 밖으로 나옵니다. – 2to1mux

+0

Wireshark가 실행되는 동안 프로그램을 수천 번 반복 실행합니다. 프로그램이 끝나면 Wireshark 로그를 조사하고 거기에서 측정 한 내용을 내 프로그램에서 인쇄 된 것과 비교합니다. 그들은 매우 유사합니다. 나는 스크린 캡쳐를 붙일 수 있었다 ... 솔직히 이유를 생각할 수 없기 때문에 이것은 나에게도 미치겠다 ... 문제는 서버에있을 수 있는가? – bsmartins

+0

화면 캡처를 게시 하시겠습니까? 나는 이론을 가지고 .. – 2to1mux

답변

0

이 인 (희망 교육)에 하나 때문이라고 믿는다 맞아,하지만 네가 뭘보고 있는지 설명 할 수있을 것 같아.

리눅스 커널이 얼마나 실시간인지 확실하지 않습니다. 완전히 선제 적이 아닐 수도 있습니다 ... 그런 면책 조항과 함께, 계속 :) ...

스케줄러에 따라 태스크는 "퀀텀"이라고 불리는 것을 가질 수 있습니다. 이것은 "우선 순위가 다른 다른 태스크가 스케줄되기 전에 실행할 수있는 시간의 양입니다." 선점 형 (pre-emptive) 인 경우 우선 순위가 높은 작업을 실행할 수있는 지점이 될 수도 있습니다. 이것은 제가 충분히 알지 못하는 스케쥴러의 세부 사항에 달려 있습니다.

첫 번째 gettime과 두 번째 gettime 사이의 어느 곳에서나 작업을 미리 수행 할 수 있습니다. 이것은 단지 "일시 중지"되어 있고 다른 작업이 일정 시간 동안 CPU를 사용한다는 것을 의미합니다.

슬립이없는 루프는이 가능성이 가장 높은 스케줄러가 현재 작업의 양자가 만료되기 전에 실행 할 수있는 지점합니다 (, 당신은에 나노초 잠을 넣어이

clock_gettime(CLOCK_MONOTONIC, &start);   
send(sock, send_buf, pkt_size, 0); 
recv(sock, recv_buf, pkt_size, 0); 
clock_gettime(CLOCK_MONOTONIC, &end); 

printf("%.3f ", timespec_diff_usec(&end, &start)); 

clock_gettime(CLOCK_MONOTONIC, &start);   

<----- PREMPTION .. your tasks quanta has run out and the scheduler kicks in 
     ... another task runs for a little while  
<----- PREMPTION again and your back on the CPU 

send(sock, send_buf, pkt_size, 0); 
recv(sock, recv_buf, pkt_size, 0); 
clock_gettime(CLOCK_MONOTONIC, &end); 

// Because you got pre-empted, your time measurement is artifically long 
printf("%.3f ", timespec_diff_usec(&end, &start)); 

clock_gettime(CLOCK_MONOTONIC, &start);   

<----- PREMPTION .. your tasks quanta has run out and the scheduler kicks in 
     ... another task runs for a little while  
<----- PREMPTION again and your back on the CPU 

and so on.... 

뭔가를 갈 수도 recv()도 같은 블록이 적용됩니다). 그래서 아마도 당신이 무엇을 얻을 당신이 (두 인 gettime 사이 prempted 때로는 인터리빙 어떤 종류의 다음 발생이

clock_gettime(CLOCK_MONOTONIC, &start);   
send(sock, send_buf, pkt_size, 0); 
recv(sock, recv_buf, pkt_size, 0); 
clock_gettime(CLOCK_MONOTONIC, &end); 

struct timespec nsleep = {.tv_sec = 0, .tv_nsec = 100000}; 
nanosleep(&nsleep, NULL); 

<----- PREMPTION .. nanosleep allows the scheduler to kick in because this is a pre-emption point 
     ... another task runs for a little while  
<----- PREMPTION again and your back on the CPU 

// Now it so happens that because your task got prempted where it did, the time 
// measurement has not been artifically increased. Your task then can fiish the rest of 
// it's quanta 
printf("%.3f ", timespec_diff_usec(&end, &start)); 

clock_gettime(CLOCK_MONOTONIC, &start);   
... and so on 

같은)의 때로는 그 밖의 때문에 nanosleep를 중입니다. x에 따라, 우연히 발생하는 곳을 (우연히) 칠 수 있습니다. 선점 포인트를 평균적으로 시간 측정 블록 밖으로 가져올 수 있습니다.

어쨌든, 즉, 내 두 페니 가치가 희망 그것은 내가 하나가 "에주의 할 필요가 생각 ...와

을 완료"나노 "물건 :

약간의 메모를 설명하는 데 도움이 nanoseconds "수면. 내가 말하는 이유는 특수한 하드웨어를 사용하지 않는 한 보통의 컴퓨터가 실제로 이것을 할 수 없다고 생각하기 때문입니다.

일반적으로 OS에는 일반적으로 5ms가 소요되는 일반 시스템 "틱"이 있습니다. 이것은 RTC (실시간 클럭 - 하드웨어가 약간 있음)에 의해 생성되는 인터럽트입니다. 이 "틱"을 사용하면 시스템이 내부 시간 표현을 생성합니다. 따라서 평균 OS는 몇 밀리 초의 시간 분해능만을가집니다. 이 틱이 더 빠르지 않은 이유는 매우 정확한 시간을 유지하고 타이머 인터럽트로 시스템을 습격하지 않는 것 사이에 균형이 이루어져야하기 때문입니다.

평균적인 최신 PC의 버전이 오래 됐는지 확신 할 수 없습니다. 그 중 일부는 고해상도 타이머가 있지만 여전히 나노초 범위가 아니며 100uS로 고생 할 수도 있습니다.

따라서 요약하면 얻을 가능성이있는 최상의 시간 해상도는 일반적으로 밀리 초 범위입니다.

편집 : 그냥이를 다시 방문하고 난

으로

가 인 nanosleep의 타이밍 정확도가 언급 한 ... (가) ... 다음은 무엇을 당신의 보는 설명하지 않고 조사를위한 또 다른 수단을 제공 할 수 있습니다 추가 거라고 생각했다 밀리 세컨드보다 좋지는 않을 것이다. 또한 작업을 미리 수행하여 타이밍 문제를 일으킬 수 있습니다. 또한 패킷이 프로토콜 스택을 올라가는 데 걸리는 시간과 네트워크 지연이 다를 수 있다는 문제도 있습니다.

NIC가 지원하는 경우 IEEE1588 (일명 PTP)을 시도해 볼 수 있습니다. NIC에서 지원하는 경우 PTP 이벤트 패킷이 PHY를 떠나 PHY에 들어갈 때 해당 패킷을 타임 스탬프 할 수 있습니다. 이것은 네트워크 지연의 견적을 당신에게 줄 것입니다. 이것은 소프트웨어 선점 등으로 인해 생길 수있는 문제를 제거합니다.나는 리눅스 PTP에 대해 몹시 두려워한다.하지만 너는 시도 할 수있다. http://linuxptp.sourceforge.net/

+0

더 많은 정보를 추가했습니다. 나는 나의 클라이언트 컴퓨터가 RTC를 가지고 있다고 믿는다 (i7 HP 컴퓨터 다.). 실시간 커널 (CONFIG_PREEMPT_RT_FULL로 구성된 커널)을 함께 사용하면 수백 또는 심지어 수십 나노초의 범위를 실현할 수 있습니다. 나는 이것이 두 clock_gettime 사이의 선점 결과라고 믿지 않습니다. 왜냐하면 그것은 일관되게 (즉, 모든 반복에서) 발생하기 때문입니다 ... – bsmartins

+0

@bsmartins 괜찮습니다. 불행히도 그것이 내 유일한 생각이었습니다 :) 당신이 솔루션을 찾으면 그것을 게시하십시오 ... 그것은 그것을 일으킨 것을 보는 것이 흥미로울 것입니다. – Jimbo

+0

리눅스 커널은 정확하게 이것을 할 수 없습니다. 프로세스가 언제든지 스왑 아웃 될 수 있기 때문에 커널에서 시간의 가치를 얻는 것은 무의미합니다. 네트워크 패킷이 들어올 때 프로세스가 차단되면 프로세스가 패킷이 있더라도 더 오래 블록 될 수 있기 때문입니다 네트워크 버퍼에 앉아. 나는 NIC가 BIOS 인터페이스에서 RTC를 사용하여 시간을 추적 할 수 있다고 믿는다. 커널은 보호 모드에서부터 액세스 할 수 없다. – Magn3s1um

0

나는 'quanta'가 가장 좋은 이론이라고 생각한다. 리눅스에서는 컨텍스트 전환 빈도입니다. 커널이 퀀텀 시간을 처리합니다.

  • 양자 시간이
  • 하드웨어 인터럽트가오고 종료

    1. 프로세스 호출 시스템 프로 시저 (등 네트워크, 하드 디스크, USB, 시계,에서 ...)
    : 그러나 과정은 두 가지 상황에서 선점

    미사용 퀀텀 시간은 우선 순위를 사용하여, 처리를 실행하는 다른 준비에 할당/RT 사실 콘텍스트 스위치 주파수는 초당 10,000 시간 구성된

    , 그것은 약 100?

    제공 등 콴타에게. 내용 전환에는 시간이 걸리며, CPU가 의존합니다.이 내용을보십시오 : http://blog.tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html 나는 내용이 부족한 이유는 무엇인가요?하지만 리눅스 커널 포럼에 대한 토론입니다.

    부분적으로 비슷한 문제가 여기에서 찾을 수 있습니다 응용 프로그램에 의해 전송되는 데이터의 양이 크고 빠른만큼 https://serverfault.com/questions/14199/how-many-context-switches-is-normal-as-a-function-of-cpu-cores-or-other

  • +0

    루프의 측정 된 부분에는 두 개의 컨텍스트 스위치가 있기 때문에 (컨텍스트 전환을 일으키는 send() 및 recv() 모두) 문제를 설명하지는 않습니다. 외부에서 잠을 자면 루프의 시간에 영향을 미치지 않습니다. – ash

    0

    경우는 각 지연에 이르게 커널 버퍼를 충전 할 수 보내(). 수면은 측정 된 섹션 밖에 있기 때문에 그렇지 않으면 send() 호출을 차단하는 데 소요되는 시간을 먹게됩니다.

    이 경우를 확인하는 데 도움이되는 한 가지 방법은 상대적으로 적은 수의 반복 횟수와 중간 정도의 반복 횟수로 실행하는 것입니다. 문제가 작은 패킷 크기 (예 : < 1k)로 반복 횟수 (예 : 20)로 발생하면 잘못된 진단 일 가능성이 있습니다.

    이렇게 빡빡한 루프로 데이터를 보내면 프로세스와 커널이 네트워크 어댑터와 이더넷 (또는 다른 미디어 유형)의 회선 속도를 쉽게 압도 할 수 있습니다.

    스크린 샷을 읽는 데 문제가 있습니다. wireshark가 전선에서 일정한 전송 속도를 보이는다면 이것이 올바른 진단이라고 제시합니다. 물론 회선 속도를 패킷 크기 (+ 헤더)로 나눈 수학을 수행하면 패킷을 보낼 수있는 최대 속도를 알 수 있습니다.

    지연 증가로 이어지는 700 마이크로 초는 결정하기가 더 어렵습니다. 나는 그것에 대해 어떤 생각도 갖고 있지 않다.

    +0

    뭐라고 요? 난 정말 궁금해. – ash

    +0

    나는 그 시스템을 다시 설정하고 이론을 테스트 할 것이다. 그러나 패킷이 1K 미만 이었지만 몇 번의 상호 작용으로 인해 발생했는지 실제로 기억하지 못합니다. – bsmartins

    +0

    좋은 사운드 - 결과를 기다리겠습니다! – ash

    2

    좋아요, 결론에 도달했습니다.

    서버에서 NetBSD 대신 Linux를 사용하여 프로그램을 시험해 보았습니다. 예상대로 실행되었는데, 즉 코드의 해당 지점에서 어느 정도 자게 되더라도 결과는 같습니다.

    이 사실은이 문제가 NetBSD의 인터페이스 드라이버에있을 수 있음을 알려줍니다. 드라이버를 확인하려면 dmesg 출력을 읽었습니다. 당신이 볼 수있는

    wm0 at pci0 dev 25 function 0: 82801I mobile (AMT) LAN Controller, rev. 3 
    wm0: interrupting at ioapic0 pin 20 
    wm0: PCI-Express bus 
    wm0: FLASH 
    wm0: Ethernet address [OMMITED] 
    ukphy0 at wm0 phy 2: Generic IEEE 802.3u media interface 
    ukphy0: OUI 0x000ac2, model 0x000b, rev. 1 
    ukphy0: 10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, 1000baseT, 1000baseT-FDX, auto 
    

    그래서, 내 인터페이스는 wm0라고 :이 관련 부분입니다. this (9 페이지)에 따르면 어떤 드라이버가로드되었는지 확인하려면 sys/dev/pci/files.pci 줄 625 (here) 파일을 참조하십시오.그것은 보여줍니다 :이 1500 값을 변경 다음

    /* 
    * For N interrupts/sec, set this value to: 
    * 1000000000/(N * 256). Note that we set the 
    * absolute and packet timer values to this value 
    * divided by 4 to get "simple timer" behavior. 
    */ 
    
    sc->sc_itr = 1500;    /* 2604 ints/sec */ 
    CSR_WRITE(sc, WMREG_ITR, sc->sc_itr); 
    

    :

    # Intel i8254x Gigabit Ethernet 
    device wm: ether, ifnet, arp, mii, mii_bitbang 
    attach wm at pci 
    file dev/pci/if_wm.c   wm 
    

    을 그리고, 드라이버 소스 코드 (dev/pci/if_wm.c, here)를 통해 검색, 나는 드라이버의 동작을 변경할 수있는 코드의 조각을 발견 1 (허용 된 초당 인터럽트 수를 늘리려고 시도) 및 0 (모두 인터럽트 조절을 제거하려고 시도 함)이 값은 모두 동일한 결과를 나타냅니다.

    • nanosleep를하지 않고 : (100) 우리의 nanosleep를 가진 ~ 400 우리에게
    • 의 대기 시간 : 200 우리의 nanosleep를 가진 ~ 230 우리에게
    • 의 대기 시간 : 260 우리의 nanosleep를 가진 ~ 120 우리
    • 의 대기 시간 : 대기 시간 의 우리가 270의 nanosleep를 가진
    • ~ 70 우리에게 : ~ 60의 대기 시간은 우리가 (최소 대기 시간이 내가 얻을 수있는) 300 위의 아무것도의 nanosleep를 가진
    • 우리 : ~ 420 우리에게
    이것은

    , 이전의 상황보다 적어도 행동하는 것이 더 좋습니다.

    따라서이 동작은 서버의 인터페이스 드라이버 때문인 것으로 결론을 냈습니다. 이 Single Board Computer와 관련된 프로젝트에서 NetBSD에서 Linux로 옮겨 가고 있기 때문에 다른 범죄자를 찾기 위해 더 조사 할 의향이 없습니다.

    +1

    참고 답변 :이 칩의 WMREG_ITR 레지스터 값은 드라이버 자체가 아닌 칩의 동작에만 영향을 미치며이 값은 기가비트 연결을 가정하여 다소 달라집니다. 드라이버 코드의 전반적인 맥락에서 설정이 82543 이상의 칩에만 적용된다는 점과 변경 설명에서 목표가 칩 잠그기를 방지하는 것임을 유의해야합니다.추가 튜닝은 심지어 더 새로운 칩셋에도 적절할 수 있지만 분명히 질문에 설명 된 동작과 어떤 식 으로든 관련이 없습니다. –

    0

    더 정확한 성능 측정을 만드는 방법에 대한 조언이 있습니다. RDTSC 명령어를 사용하거나 더 나은 내장 __rdtsc() 함수를 사용하십시오. 이것은 링 3 (시스템 호출 없음)을 떠나지 않고 CPU 카운터를 읽는 것을 포함합니다. gettime 함수는 거의 항상 시스템 호출을 필요로하므로 시스템 속도가 느려집니다.

    코드는 2 시스템 호출 (send/recv)을 포함하기 때문에 약간 까다 롭습니다. 그러나 일반적으로 첫 번째 측정 전에 sleep (0)을 호출하는 것이 좋습니다. 컨텍스트 스위치. 물론 성능에 민감한 기능의 매크로를 통해 시간 측정 (및 휴면()) 코드를 비활성화/활성화해야합니다.

    일부 운영 체제는 프로세스가 실행 시간 창 (예 : 절전 (0))을 해제하도록하여 프로세스 우선 순위를 높일 수 있습니다. 다음 스케줄 틱에서 실행 시간 할당량 실행을 완료하지 않았으므로 OS (전부는 아님)가 프로세스의 우선 순위를 높입니다.