转自:
http://blog.csdn.net/willon_tom/article/details/4470225 下面以程序的执行为主线,简要分析一下Iperf源代码的实现。 main函数 main函数在文件main.cpp中定义,它是程序的入口点。 /* ------------------------------------------------------------------- * 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 my_signal( SIGTERM, sig_exit ); my_signal( SIGINT, sig_exit ); signal(SIGPIPE,SIG_IGN); // perform any cleanup when quitting Iperf atexit( cleanup ); ext_Settings* ext_gSettings = new ext_Settings; Settings* gSettings = NULL; // read settings from environment variables and command-line interface gSettings = new Settings( ext_gSettings ); gSettings->ParseEnvironment(); gSettings->ParseCommandLine( argc, argv ); // start up client or server (listener) if ( gSettings->GetServerMode() == kMode_Server ) { // start up a listener theListener = new Listener( ext_gSettings ); theListener->DeleteSelfAfterRun(); // Start the server as a daemon if ( gSettings->GetDaemonMode() == true ) { theListener->runAsDaemon(argv[0],LOG_DAEMON); } theListener->Start(); if ( ext_gSettings->mThreads == 0 ) { theListener->SetDaemon(); // the listener keeps going; we terminate on user input only waitUntilQuit(); if ( Thread::NumUserThreads() > 0 ) { printf( wait_server_threads ); fflush( 0 ); } } } else if ( gSettings->GetServerMode() == kMode_Client ) { theSpeaker = new Speaker(ext_gSettings); theSpeaker->OwnSettings(); theSpeaker->DeleteSelfAfterRun(); theSpeaker->Start(); } 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 Thread::Joinall(); DELETE_PTR( gSettings ); // modified by qfeng // all done! return 0; } // end main 程序首先通过my_signal和signal函数为信号SIGTERM、SIGINT及SIGPIPE安装信号处理程序,这里的 my_signal函数在文件lib/signal.c中定义,它完成的功能与signal是一样的,但提高了兼容性。atexit函数可以使程序退出时 自动执行cleanup函数完成一些扫尾工作(主要是将服务器维护的客户信息链表释放)。ext_Settings是一个保存设定的参数的结构体,这些测 量时使用的参数或为用户通过命令行指定,或使用默认值。将信息组织为一个结构体有利于信息在各对象间共享和传递。Settings类对 ext_Settings结构和在其上进行的操作进行了封装。关于ext_Settings和Settings可以参见src/Settings.hpp 和src/Settings.cpp。在生成实例gSettings之后,main分表调用gSettings对象的ParseEnvironment方 法和ParseCommandLine方法从环境变量和命令行读取参数填充到结构ext_gSettings中。由调用的顺序可见,命令行参数的设定将覆 盖环境变量的设定。 如果是服务器模式,main生成Listener实例,DeleteSelfAfterRun方法是基类Thread实现的方法,它将成员变量 mDeleteSelf设为true,在线程的主过程(Run函数)结束之后,系统会自动释放内存空间(见对Thread类的讨论)。若用户指定 Iperf作为守护进程运行,则main调用runAsDaemon方法使自己在后台运行。Start方法启动Listener线程开始监听,在 Start方法中会调用pthread_create系统调用创建新线程,此后程序就存在两个执行线程了(见前文对Thread类的讨论)。新线程执行 Listerner类的Run函数。原线程从Start函数中返回。ext_gSettings的mThreads在客户端表示并行执行的线程数,在服务 器端表示服务器总共接收的请示数,为0表示服务器永远运行下去。若为后一种情况,main函数调用Thread基类的SetDaemon函数使自身不再收 线程接收同步机制的限制(所有的Thread类的派生类线程的执行结束可以通过Thread类的static成员函数Joinall进行同步, SetDaemon函数使调用线程不在Joinall函数的“join”范围之内,参见前文对Thread类的讨论),之后调用 waitUntilQuit函数。main函数将在waitUntilQuit函数中阻塞,直到所有的线程结束。 Iperf的服务器端结束过程比较复杂。在一般情况下,Iperf服务器将一直运行,直到用户按下中断键(Ctrl-C等)时程序退出。由于存 在多个线程,有的操作系统会把传向进程的信号传递给进程里的每一个线程,因此在主程序退出时必须考虑所有线程的同步问题,这是通过信号处理程序实现的。 在main函数的开始,为SIGTERM和SIGINT信号安装的信号处理函数是sig_exit,其代码如下(在文件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声明为静态变量,初始化为0,因为所有线程均使用同一个处理函数(同一份copy),因此当按下Ctrl-C时,至少有一个线程会 执行sig_exit函数,此时num值为0,程序调用exit退出。当任一线程调用exit函数时,整个进程(包括它的所有线程)退出。如果在程序退出 前有其他的线程也执行了sig_exit函数(在有多个线程收到同一信号的多个副本的情况下),num不为0,将不再调用exit函数。在这里对num是 否为0的检验是为了保证在有多个线程收到信号的情况下,exit函数仅被调用一次。 下面来看waitUntilQuit函数,该函数是在main函数中的服务器代码中被调用的,其代码如下(在src/main.cpp中) /* ------------------------------------------------------------------- * 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 这里的gQuit_cond是一个条件变量类的实例,条件变量类的定义和实现在lib/Condition.hpp中(所有的方法都实现为inline函数,提高效率)。它封装了POSIX线程间同步的条件变量机制。gQuit_cond的作用就是同步所有线程的结束。 在waitUntilQuit中,首先对gQuit_cond加锁,之后将SIGTERM和SIGINT的信号处理函数改为sig_quit, 之后调用条件变量类的Wait方法在gQuit_cond上等待,直到有线程调用gQuit_cond的Signal方法,之后 waitUntilQuit退出,主线程回到main函数。sig_quit的代码如下(在main.cpp中) /* ------------------------------------------------------------------- * 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类(说者监控线程)的实例,由后者负责生成其他的Client类(说者线程)事例向服务器端发送数据,之后main函数退出。但用户按下Ctrl-C时,线程组立即结束运行。 |
|
来自: MikeDoc > 《iperf源码分析》