如何提高閱讀源代碼能力_第1頁
如何提高閱讀源代碼能力_第2頁
如何提高閱讀源代碼能力_第3頁
如何提高閱讀源代碼能力_第4頁
如何提高閱讀源代碼能力_第5頁
已閱讀5頁,還剩38頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、如何提高閱讀源代碼能力來源: ChinaUnix博客日期:2005.10.08 07:54(共有0條評論 我要評論本文來自ChinaUnix博客,如果查看原文請點:在這里準備用一個例子來寫一下如何閱讀源代碼,分享一些經(jīng)驗,算是拋磚引玉吧!正文:由于工作的關系,我常常需要讀一些源代碼,并在上面做一些修改并且拿來使用,或者是借鑒其中的某些部分。可以說,open source對于程序員來說,是很有意義的事情。根據(jù)我的經(jīng)驗,讀源代碼,至少有3個好處。第一個好處是可以學習到很多編程的方法,看好的源代碼,對于提高自己的編程水平,比自己寫源代碼的幫助更大。當然不是說不用自己寫,而是說,自己寫代碼的同時,可以

2、從別人寫的好的源代碼中間學習到更多的編程方法和技巧。第二個好處是,可以提高自己把握大規(guī)模源代碼的能力。一個比較大型的程序,往往都是經(jīng)過了很多個版本很長的時間,有很多人參與開發(fā),修正錯誤,添加功能而發(fā)展起來的。所以往往源代碼的規(guī)模都比較大,少則10-100多k, 多的有好幾十個MB. 在閱讀大量源代碼的時候,能夠提高自己對大的軟件的把握能力,快速了解脈絡,熟悉細節(jié),不僅僅是編程技巧,還能在程序的架構,設計方面提高自己的能力。(這里說一句題外話,這本書相信很多人都看過,而且很多人對它推崇備至,奉為經(jīng)典。現(xiàn)在也出了不少書,都是冠以設計模式這一名稱。在書中就提到,設計模式并不是一本教材,不是教你如何去

3、編程序,而是把平時編程中一些固定的模式記錄下來,加以不斷的測試和改進,分發(fā)給廣大程序員的一些經(jīng)驗之談。我在看這本書的時候,有一些地方一些設計方法往往讓我有似曾相識的感覺,另外一些則是我以前就常常用到的。而這些經(jīng)驗的獲得,一部分得益于自己的編碼過程,另外一個很重要的來源就是閱讀別人寫的源代碼。閱讀源代碼第三個好處,就是獲得一些好的思想。比如,有很多人在開始一個軟件項目之前都喜歡到上去找一下,是否有人以前做過相同或者相似的軟件,如果有,則拿下來讀一讀,可以使自己對這個軟件項目有更多更深的認識。我以前曾經(jīng)想找一本關于如何閱讀源代碼的書來看看,卻沒有找到。相反,倒是找到了不少分析源代碼的書,比如Lin

4、ux kernel, Apache source, 等等。所以我想,為什么不自己來寫下一些經(jīng)驗和大家交流呢?(當然不是寫書,沒有那個能力也沒有那個時間。所以在這里我準備用一個例子來寫一下如何閱讀源代碼,分享一些經(jīng)驗,算是拋磚引玉吧!我找的例子是一個統(tǒng)計日志的工具,webalizer. (這個工具我以前用過,似乎記得以前的版本是用perl 寫的,不知道現(xiàn)在為什么作者把它完全修改成了C,可能是為了效率,也可能根本就是我記錯了。之所以選擇這個軟件來作為例子,一方面是因為它是用C寫的,流程比較簡單,沒有C+的程序那么多的枝節(jié),而且軟件功能不算復雜,代碼規(guī)模不大,能夠在一篇文章的篇幅里面講完; 另外一個

5、方面是因為恰巧前段時間我因為工作的關系把它拿來修改了一下,剛看過,還沒有忘記。:-我采用的例子是webalizer2.01-09, 也可以到它的網(wǎng)站下載最新的版本。這是一個用C寫的,處理文本文件(簡單的說是這樣,實際上它支持三種日志文本格式: CLF, FTP, SQUID, 并且用html的方式輸出結果。讀者可以自己去下載它的源代碼包,并一邊讀文章,一邊看程序。解壓縮它的tar包(我download的是它的源代碼tar包,在文件目錄中看到這樣的結果: $ lsaclocal.m4 dns_resolv.c lang output.h webalizer.1CHANGES dns_resolv

6、.h lang.h parser.c webalizer.cconfigure graphs.c linklist.c parser.h webalizer.hconfigure.in graphs.h linklist.h preserve.c webalizer_lang.hCOPYING hashtab.c Makefile.in preserve.h webalizer.LSMCopyright hashtab.h Makefile.std README webalizer.pngcountry-codes.txt INSTALL msfree.png README.FIRSTDNS.

7、README install-sh output.c sample.conf首先,我閱讀了它的README(這是很重要的一個環(huán)節(jié), 大體了解了軟件的功能,歷史狀況,修改日志,安裝方法等等。然后是安裝并且按照說明中的缺省方式來運行它,看看它的輸出結果。(安裝比較簡單,因為它帶了一個configure, 在沒有特殊情況出現(xiàn)的時候,簡單的./configure, make, make install 就可以安裝好。然后就是閱讀源代碼了。我從makefile開始入手(我覺得這是了解一個軟件的最好的方法在makefile開頭,有這些內(nèi)容:prefix = /usr/localexec_prefix =

8、$prefixBINDIR = $exec_prefix/binMANDIR = $prefix/man/man1ETCDIR = /etcCC = gccCFLAGS = -Wall -O2LIBS = -lgd -lpng -lz -lmDEFS = -DETCDIR=/etc -DHAVE_GETOPT_H=1 -DHAVE_MATH_H=1LDFLAGS=INSTALL= /usr/bin/install -cINSTALL_PROGRAM=$INSTALLINSTALL_DATA=$INSTALL -m 644# where are the GD header files?GDLIB

9、=/usr/include這些定義了安裝的路徑,執(zhí)行程序的安裝路徑,編譯器,配置文件的安裝路徑,編譯的選項,安裝程序,安裝程序的選項等等。要注意的是,這些并不是軟件的作者寫的,而是./configure的輸出結果。呵呵. :-下面才是主題內(nèi)容,也是我們關心的。# Shouldnt have to touch below here!all: webalizerwebalizer: webalizer.o webalizer.h hashtab.o hashtab.hlinklist.o linklist.h preserve.o preserve.hdns_resolv.o dns_resolv

10、.h parser.o parser.houtput.o output.h graphs.o graphs.h lang.hwebalizer_lang.h$(CC $LDFLAGS -o webalizer webalizer.o hashtab.o linklist.o preserve.o parser.o output.o dns_resolv.o graphs.o $LIBSrm -f webazolverln -s webalizer webazolverwebalizer.o: webalizer.c webalizer.h parser.h output.h preserve.

11、hgraphs.h dns_resolv.h webalizer_lang.h$(CC $CFLAGS $DEFS -c webalizer.cparser.o: parser.c parser.h webalizer.h lang.h$(CC $CFLAGS $DEFS -c parser.chashtab.o: hashtab.c hashtab.h dns_resolv.h webalizer.h lang.h$(CC $CFLAGS $DEFS -c hashtab.clinklist.o: linklist.c linklist.h webalizer.h lang.h$(CC $C

12、FLAGS $DEFS -c linklist.coutput.o: output.c output.h webalizer.h preserve.hhashtab.h graphs.h lang.h$(CC $CFLAGS $DEFS -c output.cpreserve.o: preserve.c preserve.h webalizer.h parser.hhashtab.h graphs.h lang.h$(CC $CFLAGS $DEFS -c preserve.cdns_resolv.o: dns_resolv.c dns_resolv.h lang.h webalizer.h$

13、(CC $CFLAGS $DEFS -c dns_resolv.cgraphs.o: graphs.c graphs.h webalizer.h lang.h$(CC $CFLAGS $DEFS -I$GDLIB -c graphs.c好了,不用再往下看了,這些就已經(jīng)足夠了。從這里我們可以看到這個軟件的幾個源代碼文件和他們的結構。webalizer.c是主程序所在的文件,其他的是一些輔助程序模塊。對比一下目錄里面的文件, $ ls *.c *.hdns_resolv.c graphs.h lang.h output.c parser.h webalizer.cdns_resolv.h hash

14、tab.c linklist.c output.h preserve.c webalizer.hgraphs.c hashtab.h linklist.h parser.c preserve.h webalizer_lang.h于是,讓我們從webalizer.c開始吧。作為一個C程序,在頭文件里面,和C文件里面定義的extern變量,結構等等肯定不會少,但是,單獨看這些東西我們不可能對這個程序有什么認識。所以,從main函數(shù)入手,逐步分析,在需要的時候再回頭來看這些數(shù)據(jù)結構定義才是好的方法。(順便說一句,Visual C+, 等windows下的IDE工具提供了很方便的方法來獲取函數(shù)列表,C

15、+的類列表以及資源文件,對于閱讀源代碼很有幫助。Unix/Linux 也有這些工具,但是,我們在這里暫時不說,而只是通過最簡單的文本編輯器vi來講。跳過webalizer.c 開頭的版權說明部分(GPL的,和數(shù)據(jù)結構定義,全局變量聲明部分,直接進入main(函數(shù)。在函數(shù)開頭,我們看到:/* initalize epoch */epoch=jdate(1,1,1970; /* used for timestamp adj. */* add default index. alias */add_nlist(index.,&index_alias;這兩個函數(shù)暫時不用仔細看,后面會提到,略過。spri

16、ntf(tmp_buf,%s/webalizer.conf,ETCDIR;/* check for default config file */if (!access(webalizer.conf,F_OKget_config(webalizer.conf;else if (!access(tmp_buf,F_OKget_config(tmp_buf;從注釋和程序本身可以看出,這是查找是否存在一個叫做webalizer.conf的配置文件,如果當前目錄下有,則用get_config來讀入其中內(nèi)容,如果沒有,則查找ETCDIR/webalizer.conf是否存在。如果都沒有,則進入下一部分。(

17、注意:ETCDIR = ETCDIR在makefile中有定義/* get command line options */opterr = 0; /* disable parser errors */while (i=getopt(argc,argv,a:A:c:C:dD:e:E:fF:g:GhHiI:l:Lm:M:n:N:o:pP:qQr:R:s:S:t:Tu:U:vVx:XY!=EOF switch (icase a: add_nlist(optarg,&hidden_agents; break;/* Hide agents */case A: ntop_agents=atoi(optar

18、g; break;/* Top agents */case c: get_config(optarg; break;/* Config file */case C: ntop_ctrys=atoi(optarg; break;/* Top countries */case d: debug_mode=1; break;/* Debug */case D: dns_cache=optarg; break;/* DNS Cache filename */case e: ntop_entry=atoi(optarg; break;/* Top entry pages */case E: ntop_e

19、xit=atoi(optarg; break;/* Top exit pages */case f: fold_seq_err=1; break;/* Fold sequence errs */case F: log_type=(optarg0=f?LOG_FTP:(optarg0=s?LOG_SQUID:LOG_CLF; break;/* define log type */case g: group_domains=atoi(optarg; break; /* GroupDomains (0=no */case G: hourly_graph=0; break;/* no hourly g

20、raph */case h: print_opts(argv0; break;/* help */case H: hourly_stats=0; break;/* no hourly stats */case i: ignore_hist=1; break;/* Ignore history */case I: add_nlist(optarg,&index_alias; break; /* Index alias */case l: graph_lines=atoi(optarg; break;/* Graph Lines */case L: graph_legend=0; break;/*

21、 Graph Legends */case m: visit_timeout=atoi(optarg; break;/* Visit Timeout */case M: mangle_agent=atoi(optarg; break; /* mangle user agents */case n: hname=optarg; break;/* Hostname */case N: dns_children=atoi(optarg; break;/* # of DNS children */case o: out_dir=optarg; break;/* Output directory */c

22、ase p: incremental=1; break;/* Incremental run */case P: add_nlist(optarg,&page_type; break; /* page view types */case q: verbose=1; break;/* Quiet (verbose=1 */case Q: verbose=0; break;/* Really Quiet */case r: add_nlist(optarg,&hidden_refs; break; /* Hide referrer */case R: ntop_refs=atoi(optarg;

23、break;/* Top referrers */case s: add_nlist(optarg,&hidden_sites; break; /* Hide site */case S: ntop_sites=atoi(optarg; break;/* Top sites */case t: msg_title=optarg; break;/* Report title */case T: time_me=1; break; /* TimeMe */case u: add_nlist(optarg,&hidden_urls; break; /* hide URL */case U: ntop

24、_urls=atoi(optarg; break;/* Top urls */case v:case V: print_version(; break;/* Version */case x: html_ext=optarg; break;/* HTML file extension */case X: hide_sites=1; break;/* Hide ind. sites */case Y: ctry_graph=0; break;/* Supress ctry graph */if (argc - optind != 0 log_fname = argvoptind;if ( log

25、_fname & (log_fname0=- log_fname=NULL;/* force STDIN? */* check for gzipped file - .gz */if (log_fname if (!strcmp(log_fname+strlen(log_fname-3,.gzgz_log=1;這一段是分析命令行參數(shù)及開關。(getopt(的用法我在另外一篇文章中講過,這里就不再重復了??梢钥吹?這個軟件雖然功能不太復雜,但是開關選項還是不少。大多數(shù)的unix/linux程序的開頭部分都是這個套路,初始化配置文件,并且讀入分析命令行。在這段程序中,我們需要注意一個函數(shù):add_

26、nlist(. print_opts(, get_config(等等一看就明白,就不用多講了。這里我們已經(jīng)是第二次遇到add_nlist這個函數(shù)了,就仔細看看吧。$ grep add_nlist *.hlinklist.h:extern int add_nlist(char *, NLISTPTR *;/* add list item */可以發(fā)現(xiàn)它定義在linklist.h中。在這個h文件中,當然會有一些數(shù)據(jù)結構的定義,比如:struct nlist char string80;/* list struct for HIDE items */struct nlist *next; ;type

27、def struct nlist *NLISTPTR;struct glist char string80;/* list struct for GROUP items */char name80;struct glist *next; ;typedef struct glist *GLISTPTR;這是兩個鏈表結構。還有extern GLISTPTR group_sites ; /* group lists */extern GLISTPTR group_urls ;extern GLISTPTR group_refs ;這些都是鏈表,太多了,不用一一看得很仔細,因為目前也看不出來什么東西。

28、當然要注意它們是extern的,也就是說,可以在其他地方(文件看到它們的數(shù)值(類似于C+中的public變量。這里還定義了4個函數(shù):extern char *isinlist(NLISTPTR, char *;/* scan list for str */extern char *isinglist(GLISTPTR, char *;/* scan glist for str */extern int add_nlist(char *, NLISTPTR *;/* add list item */extern int add_glist(char *, GLISTPTR *;/* add gr

29、oup list item */注意,這些都是extern的,也就是說,可以在其他地方見到它們的調(diào)用(有點相當于C+中的public 函數(shù)。再來看看linklist.c,NLISTPTR new_nlist(char *; /* new list node */void del_nlist(NLISTPTR *; /* del list */GLISTPTR new_glist(char *, char *; /* new group list node */void del_glist(GLISTPTR *; /* del group list */int isinstr(char *, c

30、har *;這5個函數(shù)是內(nèi)部使用的(相當于C+中的private, 也就是說,這些函數(shù)只被isinlist(NLISTPTR, char *, isinglist(GLISTPTR, char *, add_nlist(char *, NLISTPTR *, add_glist(char *, GLISTPTR *調(diào)用,而不會出現(xiàn)在其他地方。所以,我們先來看這幾個內(nèi)部函數(shù)。舉例來說,add_nlist(char *NLISTPTR new_nlist(char *strNLISTPTR newptr;if (sizeof(newptr-string string, str, sizeof(ne

31、wptr-string;newptr-next=NULL;return newptr;這個函數(shù)分配了一個struct nlist, 并且把其中的string賦值為str, next賦值為NULL.這實際上是創(chuàng)建了鏈表中的一個節(jié)點。verbose是一個全局變量,定義了輸出信息的類型,如果verbose為1,則輸出很詳細的信息,否則輸出簡略信息。這是為了調(diào)試或者使用者詳細了解程序情況來用的。不是重要內(nèi)容,雖然我們常??梢栽谶@個源程序的其他地方看到它。另外一個函數(shù):void del_nlist(NLISTPTR *listNLISTPTR cptr,nptr;cptr=*list;while (cp

32、tr!=NULLnptr=cptr-next;free(cptr;cptr=nptr;這個函數(shù)刪除了一個nlist(也可能是list所指向的那一個部分開始知道鏈表結尾,比較簡單。看完了這兩個內(nèi)部函數(shù),可以來看/*/* ADD_NLIST - add item to FIFO linked list */*/int add_nlist(char *str, NLISTPTR *listNLISTPTR newptr,cptr,pptr;if ( (newptr = new_nlist(str != NULLif (*list=NULL *list=newptr;elsecptr=pptr=*li

33、st;while(cptr!=NULL pptr=cptr; cptr=cptr-next; ;pptr-next = newptr;return newptr=NULL;這個函數(shù)是建立了一個新的節(jié)點,把參數(shù)str賦值給新節(jié)點的string, 并把它連接到list所指向鏈表的結尾。另外的三個函數(shù):new_glist(, del_glist(, add_glist(完成的功能和上述三個差不多,所不同的只是它們所處理的數(shù)據(jù)結構不同??赐炅诉@幾個函數(shù),我們回到main程序。接下來是,/* setup our internal variables */init_counters(; /* inital

34、ize main counters */我們所閱讀的這個軟件是用來分析日志并且做出統(tǒng)計的,那么這個函數(shù)的名字已經(jīng)告訴了我們,這是一個初始化計數(shù)器的函數(shù)。簡略的看看吧!$ grep init_counters *.hwebalizer.h:extern void init_counters(;在webalizer.c中找到:void init_counters(int i;for (i=0;i根據(jù)在最開始讀過的README文件,這個page_type是用來定義處理的頁面的類型的。在README 文件中,-P name Page type. This is the extension of fil

35、es you consider tobe pages for Pages calculations (sometimes called pageviews.The default is htm* and cgi (plus whatever HTMLExtensionyou specified if it is different. Dont use a period!我們在程序中也可以看到,如果沒有在命令行中或者config文件中指定,則根據(jù)處理的日志文件的類型來添加缺省的文件類型。比如對于CLF文件(WWW日志,處理html, htm, cgi文件if (log_type = LOG_FT

36、P/* disable stuff for ftp logs */ntop_entry=ntop_exit=0;ntop_search=0;.這一段是對于FTP的日志格式,設置搜索列表。for (i=0;i清空哈西表,為下面即將進行的排序工作做好準備。關于哈西表,這是數(shù)據(jù)結構中常用的一種用來快速排序的結構,如果不清楚,可以參考相關書籍,比如清華的教材或者等書。if (verbose1uname(&system_info;printf(Webalizer V%s-%s (%s %s %s ,version,editlvl,system_info.sysname,system_info.relea

37、se,language;這一段,是打印有關系統(tǒng)的信息和webalizer程序的信息(可以參考uname的函數(shù)說明。#ifndef USE_DNSif (strstr(argv0,webazolver!=0printf(DNS support not present, aborting. ;exit(1;#endif /* USE_DNS */這一段,回憶我們在看README文件的時候,曾經(jīng)提到過可以在編譯的時候設置選項開關來設定DNS 支持,在源代碼中可以看到多次這樣的代碼段出現(xiàn),如果不指定DNS支持,這些代碼段則會出現(xiàn)(ifdef或者不出現(xiàn)(ifndef.下面略過這些代碼段,不再重復。/*

38、open log file */if (gz_loggzlog_fp = gzopen(log_fname,rb;if (gzlog_fp=Z_NULL/* Error: Cant open log file . */fprintf(stderr, %s %s ,msg_log_err,log_fname;exit(1;elseif (log_fnamelog_fp = fopen(log_fname,r;if (log_fp=NULL/* Error: Cant open log file . */fprintf(stderr, %s %s ,msg_log_err,log_fname;ex

39、it(1;這一段,回憶在README文件中曾經(jīng)讀到過,如果log文件是gzip壓縮格式,則用gzopen函數(shù)打開(可以猜想gz*是一套針對gzip壓縮格式的實時解壓縮函數(shù),如果不是,則用fopen打開。/* switch directories if needed */if (out_dirif (chdir(out_dir != 0/* Error: Cant change directory to . */fprintf(stderr, %s %s ,msg_dir_err,out_dir;exit(1;同樣,回憶在README文件中讀到過,如果參數(shù)行有-o out_dir, 則將輸出結果

40、到該目錄,否則,則輸出到當前目錄。在這一段中,如果輸出目錄不存在(chdir(out_dir != 0則出錯。#ifdef USE_DNSif (strstr(argv0,webazolver!=0if (!dns_children dns_children=5; /* default dns children if needed */if (!dns_cache/* No cache file specified, aborting. */fprintf(stderr,%s ,msg_dns_nocf; /* Must have a cache file */exit(1;.在上面曾經(jīng)提到過

41、,這是DNS解析的代碼部分,可以略過不看,不會影響對整個程序的理解。/* prep hostname */if (!hnameif (uname(&system_info hname=localhost;else hname=system_info.nodename;這一段繼續(xù)處理參數(shù)做準備工作。如果在命令行中指定了hostname(機器名則采用指定的名稱,否則調(diào)用uname查找機器名,如果沒有,則用localhost來作為機器名。(同樣在README中說得很詳細/* get past history */if (ignore_hist if (verbose1 printf(%s ,msg_

42、ign_hist; else get_history(;如果在命令行中指定了忽略歷史文件,則不讀取歷史文件,否則調(diào)用get_history(來讀取歷史數(shù)據(jù)。在這里,我們可以回想在README文件中同樣說過這一細節(jié),在命令行或者配置文件中都能指定這一開關。需要說明的是,我們在這里并不一定需要去看get_history這一函數(shù),因為從函數(shù)的名稱,README文件和程序注釋都能很清楚的得知這一函數(shù)的功能,不一定要去看代碼。而如果要猜想的話,也可以想到, history是webalizer在上次運行的時候記錄下來的一個文件,而這個文件則是去讀取它,并將它的數(shù)據(jù)包括到這次的分析中去。不信,我們可以來看看

43、。void get_history(int i,numfields;FILE *hist_fp;char bufferBUFSIZE;/* first initalize internal array */for (i=0;i1 printf(%s %s ,msg_get_hist,hist_fname;while (fgets(buffer,BUFSIZE,hist_fp != NULLi = atoi(buffer -1;if (i11if (verbosefprintf(stderr,%s (mth=%d ,msg_bad_hist,i+1;continue;/* month# year

44、# requests files sites xfer firstday lastday */ numfields = sscanf(buffer,%d %d %lu %lu %lu %lf %d %d %lu %lu, &hist_month,&hist_year,&hist_hit,&hist_files,&hist_site,&hist_xfer,&hist_fday,&hist_lday,&hist_page,&hist_visit;if (numfields=8 /* kludge for reading 1.20.xx history files */hist_page = 0;h

45、ist_visit = 0;fclose(hist_fp;else if (verbose1 printf(%s ,msg_no_hist;/*/* PUT_HISTORY - write out history file */*/void put_history(int i;FILE *hist_fp;hist_fp = fopen(hist_fname,w;if (hist_fpif (verbose1 printf(%s ,msg_put_hist;for (i=0;i在preserve.c中,這兩個函數(shù)是成對出現(xiàn)的。get_history(讀取文件中的數(shù)據(jù),并將其記錄到hist_開頭的

46、一些數(shù)組中去。而put_history(則是將一些數(shù)據(jù)記錄到同樣的數(shù)組中去。我們可以推測得知,hist_數(shù)組是全局變量(在函數(shù)中沒有定義,也可以查找源代碼驗證。同樣,我們可以找一找put_history(出現(xiàn)的地方,來驗證剛才的推測是否正確。在webalizer.c的1311行,出現(xiàn):month_update_exit(rec_tstamp; /* calculate exit pages */write_month_html(; /* write monthly HTML file */write_main_index(; /* write main HTML file */put_hist

47、ory(; /* write history */可以知道,推測是正確的。再往下讀代碼,if (incremental /* incremental processing? */if (i=restore_state( /* restore internal data structs */* Error: Unable to restore run data (error num */* if (verbose fprintf(stderr,%s (%d ,msg_bad_data,i; */fprintf(stderr,%s (%d ,msg_bad_data,i;exit(1;.同樣,這也

48、是處理命令行和做數(shù)據(jù)準備,而且和get_history(, put_history(有些類似,讀者可以自己練習一下。下面,終于進入了程序的主體部分, 在做完了命令行分析,數(shù)據(jù)準備之后,開始從日志文件中讀取數(shù)據(jù)并做分析了。/*/* MAIN PROCESS LOOP - read through log file */*/while ( (gz_log?(our_gzgets(gzlog_fp,buffer,BUFSIZE != Z_NULL:(fgets(buffer,BUFSIZE,log_fname?log_fp:stdin != NULL我看到這里的時候,頗有一些不同意作者的這種寫法。這

49、一段while中的部分寫的比較復雜而且效率不高。因為從程序推斷和從他的代碼看來,作者是想根據(jù)日志文件的類型不同來采用不同的方法讀取文件,如果是gzip格式,則用our_gzgets來讀取其中一行,如果是普通的文本文件格式,則用fgets(來讀取。但是,這段代碼是寫在while循環(huán)中的,每次讀取一行就要重復判斷一次,明顯是多余的而且降低了程序的性能??梢栽趙hile循環(huán)之前做一次這樣的判斷,然后就不用重復了。total_rec+;if (strlen(buffer = (BUFSIZE-1if (verbosefprintf(stderr,%s,msg_big_rec;if (debug_mod

50、e fprintf(stderr,: %s,buffer;else fprintf(stderr, ;total_bad+; /* bump bad record counter */* get the rest of the record */while ( (gz_log?(our_gzgets(gzlog_fp,buffer,BUFSIZE!=Z_NULL:(fgets(buffer,BUFSIZE,log_fname?log_fp:stdin!=NULLif (strlen(buffer這一段代碼,讀入一行,如果這一行超過了程序允許的最大字符數(shù)(則是錯誤的日志數(shù)據(jù)紀錄,則跳過本行剩下的

51、數(shù)據(jù),忽略掉(continue進行下一次循環(huán)。同時把total_bad增加一個。如果沒有超過程序允許的最大字符數(shù)(則是正確的日志數(shù)據(jù)紀錄,則/* got a record. */strcpy(tmp_buf, buffer; /* save buffer in case of error */if (parse_record(buffer /* parse the record */將該數(shù)據(jù)拷貝到一個緩沖區(qū)中,然后調(diào)用parse_record(進行處理。我們可以同樣的推測一下, get_record(是這個程序的一個主要處理部分,分析了日志數(shù)據(jù)。在parse_record.c中,有此函數(shù), /

52、*/* PARSE_RECORD - uhhh, you know. */*/int parse_record(char *buffer/* clear out structure */memset(&log_rec,0,sizeof(struct log_struct;/*log_rec.hostname0=0;log_rec.datetime0=0;log_rec.url0=0;log_rec.resp_code=0;log_rec.xfer_size=0;log_rec.refer0=0;log_rec.agent0=0;log_rec.srchstr0=0;log_rec.ident0

53、=0;*/#ifdef USE_DNSmemset(&log_rec.addr,0,sizeof(struct in_addr;#endif/* call appropriate handler */switch (log_typedefault:case LOG_CLF: return parse_record_web(buffer; break;/* clf */case LOG_FTP: return parse_record_ftp(buffer; break;/* ftp */case LOG_SQUID: return parse_record_squid(buffer; break;/* squid */可以看到,log_rec是一個全局變量,該函數(shù)根據(jù)日志文件的類型,分別調(diào)用三種不同的分析函數(shù)。在webalizer.h中,找到該變量的定義,從結構定義中可以看到,結構定義了一個日志文件所可能包含的所有信息(參考CLF,FTP, SQUID日志文件的格式說明。/* log record structure */struct log_struct c

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論