Iperf 소스 코드 분석(9)

11833 단어
다음은 프로그램의 실행을 위주로 하여 Iperf 소스 코드의 실현을 간략하게 분석해 보겠습니다.
main 함수
main 함수 파일main.cpp에서 정의합니다. 프로그램의 입구점입니다.
#define HEADERS()
#include "headers.h"
#include "Client.hpp"
#include "Settings.hpp"
#include "Listener.hpp"
#include "Speaker.hpp"
#include "Locale.hpp"
#include "Condition.hpp"
#include "List.h"
#include "util.h"
/* -------------------------------------------------------------------
 * prototypes
 * ------------------------------------------------------------------- */

void waitUntilQuit( void );

void sig_quit( int inSigno );

void cleanup( void );

/* -------------------------------------------------------------------
 * global variables
 * ------------------------------------------------------------------- */
#define GLOBAL()

Condition gQuit_cond;

/* -------------------------------------------------------------------
 * sets up signal handlers
 * parses settings from environment and command line
 * starts up server or client thread
 * waits for all threads to complete
 * ------------------------------------------------------------------- */

int main( int argc, char **argv ) {

    Listener *theListener = NULL;
    Speaker  *theSpeaker  = NULL;

    // signal handlers quietly exit on ^C and kill
    // these are usually remapped later by the client or server
    //  SIGTERM、SIGINT          
    my_signal( SIGTERM, sig_exit );
    my_signal( SIGINT,  sig_exit );

    signal(SIGPIPE,SIG_IGN);

    // perform any cleanup when quitting Iperf
    //    Iperf   Cleanup        
    atexit( cleanup );

    ext_Settings* ext_gSettings = new ext_Settings;
    Settings* gSettings = NULL;

    // read settings from environment variables and command-line interface
    //                      
    //        argc argv 
    gSettings = new Settings( ext_gSettings );
    gSettings->ParseEnvironment();
    gSettings->ParseCommandLine( argc, argv );
    
    // start up client or server (listener)
    //           

    if ( gSettings->GetServerMode() == kMode_Server ) {
        //   iperf          
        //           :
        // socket()-->bind()-->listen()-->accept()-->......
        //    ,    Listener,socket()/bind()/listen()    
        // start up a listener
        theListener = new Listener( ext_gSettings );
        //Listener   Thread,    Thread     DeleteSelfAfterRun(),  
        //           
        theListener->DeleteSelfAfterRun();

        // Start the server as a daemon(    )
        //     ,       Daemon  , Linux        。
        //             ,                     
        //             。
        if ( gSettings->GetDaemonMode() == true ) {
            theListener->runAsDaemon(argv[0],LOG_DAEMON);
        }
    
        //   Listener  
        //    ,Listener Run               :
        // (1)、Listener  Start(), Start() ,     :
        // int err = pthread_create( &mTID, NULL, Run_Wrapper, ptr );<    
        //    >
        //    Run_Wrapper( ptr );<       >
        // (2)、Run_Wrapper( ptr ) ,
        // Thread* objectPtr = (Thread*) paramPtr;
        // objectPtr->Run();
        //   ,Run()      。         virtual             
        //          ,virtual      (    (   ) {   ;}),  
        //    ,               ,              。
        //         Listener    Run()  ,            
        theListener->Start();

        if ( ext_gSettings->mThreads == 0 ) {
            // ???   sNum                 。    !!!!!(  )
            //        ,setDaemon           sNum--,           1,
            //                  Run  ,                  
            //  (                    )。  ,Listener      , 
            //      。
            theListener->SetDaemon();

            // the listener keeps going; we terminate on user input only
            waitUntilQuit();

#ifdef HAVE_THREAD
            //    ,NumUserThreads()     sNum,        。  ,       
            //       、Run_Wrapper setDaemon。  ,                。
            if ( Thread::NumUserThreads() > 0 ) {
                printf( wait_server_threads );
                fflush( 0 );
            }
#endif
        }
    } 
    else if ( gSettings->GetServerMode() == kMode_Client ) {
        //   iperf          
#ifdef HAVE_THREAD
        // Speaker      Listener   
        //         Speaker   ,            
        theSpeaker = new Speaker(ext_gSettings);
        theSpeaker->OwnSettings();
        theSpeaker->DeleteSelfAfterRun();
        theSpeaker->Start();
#else
        // If we need to start up a listener do it now
        ext_Settings *temp = NULL;
        Settings::GenerateListenerSettings( ext_gSettings, &temp );
        if ( temp != NULL ) {
            theListener = new Listener( temp );
            theListener->DeleteSelfAfterRun();
        }
        Client* theClient = new Client( ext_gSettings );
        theClient->InitiateServer();
        theClient->Start();
        if ( theListener != NULL ) {
            theListener->Start();
        }
#endif
    } 
    else {
        // neither server nor client mode was specified
        // print usage and exit
        printf( usage_short, argv[0], argv[0] );
    }

    // wait for other (client, server) threads to complete
    // Joinall                
    Thread::Joinall();
    DELETE_PTR( gSettings );  // modified by qfeng
    // all done!
    return 0;
} // end main


/* -------------------------------------------------------------------
 * Any necesary cleanup before Iperf quits. Called at program exit,
 * either by exit() or terminating main().
 * ------------------------------------------------------------------- */
void cleanup( void ) {
    extern Iperf_ListEntry *clients;
    Iperf_destroy ( &clients );

} // end cleanup

프로그램 우선 my 통과시그널과 시그널 함수는 신호 SIGTERM, SIGINT 및 SIGPIPE에 신호 처리 프로그램을 설치합니다. 여기 mysignal 함수는 파일lib/signal에 있습니다.c에서 정의한 바와 같이 이 기능이 완성된 기능은 signal과 같지만 호환성을 향상시켰다.atexit 함수는 프로그램이 종료될 때 자동으로 cleanup 함수를 실행하여 마무리 작업을 완성할 수 있습니다. (주로 서버가 유지하는 고객 정보 체인 테이블을 방출합니다.)ext_Settings는 사용자가 명령줄을 통해 지정하거나 기본값을 사용하는 두 가지 방법으로 측정에 사용되는 매개변수를 저장하는 구조체입니다.정보를 하나의 구조체로 조직하면 정보가 각 대상 간에 공유되고 전달되는 데 유리하다.ext_Settings가 정보에 대한 집중 봉인을 실현하는 장점은 다중 클라이언트와 다중 서버 구조에 대해 ext 를 잘 전달할 수 있다는 것이다Settings 구조체로 관련 정보를 교류합니다.Settings 클래스 쌍 extSettings 구조와 그 위에서 진행된 조작이 봉인되었다.ext 정보Settings와 Settings는 src/Settings를 참고할 수 있습니다.hpp와 src/settings.cpp.실례 gSettings를 생성한 후main분표는 gSettings 대상의Parse Environment 방법과Parse CommandLine 방법을 호출하여 환경 변수와 명령줄에서 파라미터를 읽고 구조 ext 로 채웁니다gSettings에서호출된 순서에서 볼 수 있으며 명령행 매개 변수의 설정은 환경 변수의 설정을 덮어씁니다.
서버 모드라면main에서Listener를 생성하는 실례입니다. Delete Self After Run 방법은 기본 클래스 Thread가 실현하는 방법입니다. 구성원 변수 mDelete Self를true로 설정하고 루트의 주 프로세스 (Run 함수) 가 끝난 후에 시스템은 자동으로 메모리 공간을 방출합니다. (Thread 클래스에 대한 토론 참조)사용자가 Iperf를 수호 프로세스로 지정하면,main은runasDaemon 방법을 사용해서 백엔드에서 실행합니다.Start 메서드는 Listener 스레드를 시작하고 Start 메서드에서 pthread 를 호출합니다.create 시스템에서 새 루틴을 만들기 위해 호출합니다. 이후 프로그램에 두 개의 실행 루틴이 존재합니다. (앞에서 Thread 클래스에 대한 토론을 보십시오.)새 스레드는 Listerner 클래스의 런 함수를 실행합니다.Start 함수에서 원래 스레드가 반환됩니다.ext_gSettings의 mThreads는 클라이언트에서 병행적으로 실행되는 스레드 수를 표시하고 서버 측에서 서버가 모두 수신한 요청 수를 표시하며 0은 서버가 영원히 실행될 수 있음을 나타낸다.다음 경우main 함수는 Thread 기본 클래스의 SetDaemon 함수를 호출하여 자신이 스레드 수신 동기화 메커니즘의 제한을 받지 않도록 합니다. (모든 Thread 클래스의 파생 클래스 스레드의 실행 끝은 Thread 클래스의 static 구성원 함수인 Joinall을 통해 동기화할 수 있습니다. SetDaemon 함수는 호출 스레드가 Joinal 함수의'join'범위 안에 있지 않도록 합니다. 앞에서 언급한 Thread 클래스에 대한 토론을 참고하십시오.) 그 다음에 waitUntilQuit 함수를 호출합니다.main 함수는 모든 라인이 끝날 때까지 waitUntilQuit 함수에서 막힙니다.
Iperf의 서버측 종료 프로세스는 복잡합니다.일반적으로 Iperf 서버는 중단키(Ctrl-C 등)를 누를 때까지 프로그램이 종료될 때까지 실행됩니다.여러 개의 라인이 존재하기 때문에 어떤 운영체제는 프로세스로 전달되는 신호를 프로세스의 모든 라인에 전달하기 때문에 메인 프로그램이 종료될 때 모든 라인의 동기화 문제를 고려해야 한다. 이것은 신호 처리 프로그램을 통해 이루어진 것이다.
sig_exit 함수
main 함수의 시작에서 SIGTERM과 SIGINT 신호에 설치된 신호 처리 함수는sigexit, 코드는 다음과 같습니다. (파일lib/signal.c에서)
/* -------------------------------------------------------------------
 * sig_exit
 *
 * Quietly exits. This protects some against being called multiple
 * times. (TODO: should use a mutex to ensure (num++ == 0) is atomic.)
 * ------------------------------------------------------------------- */
void sig_exit( int inSigno ) {
    static int num = 0;
    if ( num++ == 0 ) {
        //        ,                    
        fflush( 0 );
        exit( 0 );
    }
} /* end sig_exit */

여기의num는 정적 변수로 초기화됩니다. 모든 스레드가 같은 처리 함수 (같은 코피) 를 사용하기 때문에, Ctrl + C를 누르면 최소한 한 스레드가sig 를 실행합니다.exit 함수, 이때num값이 0이면 프로그램이 exit를 호출하여 종료합니다.모든 루틴이 exit 함수를 호출할 때 전체 프로세스 (모든 루틴 포함) 가 종료됩니다.만약 프로그램이 종료되기 전에 다른 라인도sig 를 실행했다면exit 함수 (여러 라인이 같은 신호를 받은 여러 부본이 있는 경우),num은 0이 아니며, exit 함수를 호출하지 않습니다.여기서num이 0인지 확인하는 것은 여러 개의 라인이 신호를 받는 상황에서 exit 함수가 한 번만 호출되는 것을 보장하기 위해서입니다.
waitUntilQuit 함수
/* -------------------------------------------------------------------
 * Blocks the thread until a quit thread signal is sent
 * ------------------------------------------------------------------- */

void waitUntilQuit( void ) {
#ifdef HAVE_THREAD

    // signal handlers send quit signal on ^C and kill
    gQuit_cond.Lock();
    my_signal( SIGTERM, sig_quit );
    my_signal( SIGINT,  sig_quit );

#ifdef HAVE_USLEEP

    // this sleep is a hack to get around an apparent bug? in IRIX
    // where pthread_cancel doesn't work unless the thread
    // starts up before the gQuit_cand.Wait() call below.
    // A better solution is to just use sigwait here, but
    // then I have to emulate that for Windows...
    usleep( 10 );
#endif

    // wait for quit signal
    gQuit_cond.Wait();
    gQuit_cond.Unlock();
#endif
} // end waitUntilQuit

여기 gQuitcond는 조건 변수 클래스의 실례로 조건 변수 클래스의 정의와lib/Condition에서 이루어진다.hpp에서 (모든 방법은 inline 함수로 이루어져 효율을 높인다).POSIX 스레드 간 동기화 조건 변수 메커니즘을 캡슐화합니다.gQuit_cond의 역할은 모든 라인의 끝을 동기화하는 것이다.waitUntilQuit에서 먼저 gQuitcond 잠금을 설정한 후 SIGTERM 및 SIGINT의 신호 처리 함수를sig 로 변경합니다.quit, 이후 조건 변수 클래스를 호출하는 Wait 방법은 gQuitgQuit 스레드가 호출될 때까지 cond에서 기다립니다.cond의Signal 방법, 그 다음waitUntilQuit가 종료되고 메인 라인은main 함수로 돌아갑니다.
sig_quit 함수:
/* -------------------------------------------------------------------
 * Sends a quit thread signal to let the main thread quit nicely.
 * ------------------------------------------------------------------- */

void sig_quit( int inSigno ) {
#ifdef HAVE_THREAD

    // if we get a second signal after 1/10 second, exit
    // some implementations send the signal to all threads, so the 1/10 sec
    // allows us to ignore multiple receipts of the same signal
    static Timestamp* first = NULL;
    if ( first != NULL ) {
        Timestamp now;
        if ( now.subSec( *first ) > 0.1 ) {
            sig_exit( inSigno );
        }
    } else {
        first = new Timestamp();
    }

    // with threads, send a quit signal
    gQuit_cond.Signal();

#else

    // without threads, just exit quietly, same as sig_exit()
    sig_exit( inSigno );

#endif
} // end sig_quit

    sig_quit 완성의 주요 기능은: 어떤 라인이 첫 번째 중단 신호를 받았을 때 gQuit 를 호출하는 것입니다cond의 Signal 방법은 이 변수에서 기다리는 주 루틴을 깨웁니다. (이 때 주 루틴은waitUntilQuit 함수에서 막히고 있습니다.)만약 아주 짧은 시간(0.1s) 내에 다른 라인이sig 를 실행했다면quit 함수는 같은 신호의 사본이기 때문에 어떠한 조작도 하지 않는다.일정 시간이 지나면, 사용자가 여기에서 Ctrl-C 키를 눌렀을 때, 프로그램이 강제로 종료하도록 요구하는데, 이때sig 를 통해exit 함수 호출 exit 함수 종료.
main 함수로 돌아가서 주 루틴이waitUntilQuit에서 실행되고 있는 루틴을 발견하면 다음과 같은 문장을 인쇄합니다.
Waiting for server threads to complete. Interrupt again to force quit.

이 때 사용자가 Ctrl-C를 누르면 위에서 설명한 두 번째 상황이 발생하고 프로그램이 실행을 종료합니다.그렇지 않으면 주 스레드가 Thread 클래스의 Joinall 메서드(Joinall은 Thread 클래스의 정적 함수 구성원)를 호출하여 모든 스레드가 끝날 때까지 기다린 다음 메모리를 풀고 종료합니다.
클라이언트에서main 함수의 작업은 비교적 간단하다.이것은 Speaker 클래스(설자 감시 루트)의 실례를 생성하고 후자가 다른 클라이언트 클래스(설자 루트) 사례를 생성하여 서버에 데이터를 보내고 그 다음main 함수가 종료한다.그러나 사용자가 Ctrl-C를 누르면 스레드 그룹은 즉시 실행을 종료합니다.

좋은 웹페이지 즐겨찾기