




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、如何提高閱讀源代碼能力來(lái)源: ChinaUnix博客日期:2005.10.08 07:54(共有0條評(píng)論 我要評(píng)論本文來(lái)自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):在這里準(zhǔn)備用一個(gè)例子來(lái)寫(xiě)一下如何閱讀源代碼,分享一些經(jīng)驗(yàn),算是拋磚引玉吧!正文:由于工作的關(guān)系,我常常需要讀一些源代碼,并在上面做一些修改并且拿來(lái)使用,或者是借鑒其中的某些部分??梢哉f(shuō),open source對(duì)于程序員來(lái)說(shuō),是很有意義的事情。根據(jù)我的經(jīng)驗(yàn),讀源代碼,至少有3個(gè)好處。第一個(gè)好處是可以學(xué)習(xí)到很多編程的方法,看好的源代碼,對(duì)于提高自己的編程水平,比自己寫(xiě)源代碼的幫助更大。當(dāng)然不是說(shuō)不用自己寫(xiě),而是說(shuō),自己寫(xiě)代碼的同時(shí),可以
2、從別人寫(xiě)的好的源代碼中間學(xué)習(xí)到更多的編程方法和技巧。第二個(gè)好處是,可以提高自己把握大規(guī)模源代碼的能力。一個(gè)比較大型的程序,往往都是經(jīng)過(guò)了很多個(gè)版本很長(zhǎng)的時(shí)間,有很多人參與開(kāi)發(fā),修正錯(cuò)誤,添加功能而發(fā)展起來(lái)的。所以往往源代碼的規(guī)模都比較大,少則10-100多k, 多的有好幾十個(gè)MB. 在閱讀大量源代碼的時(shí)候,能夠提高自己對(duì)大的軟件的把握能力,快速了解脈絡(luò),熟悉細(xì)節(jié),不僅僅是編程技巧,還能在程序的架構(gòu),設(shè)計(jì)方面提高自己的能力。(這里說(shuō)一句題外話,這本書(shū)相信很多人都看過(guò),而且很多人對(duì)它推崇備至,奉為經(jīng)典?,F(xiàn)在也出了不少書(shū),都是冠以設(shè)計(jì)模式這一名稱。在書(shū)中就提到,設(shè)計(jì)模式并不是一本教材,不是教你如何去
3、編程序,而是把平時(shí)編程中一些固定的模式記錄下來(lái),加以不斷的測(cè)試和改進(jìn),分發(fā)給廣大程序員的一些經(jīng)驗(yàn)之談。我在看這本書(shū)的時(shí)候,有一些地方一些設(shè)計(jì)方法往往讓我有似曾相識(shí)的感覺(jué),另外一些則是我以前就常常用到的。而這些經(jīng)驗(yàn)的獲得,一部分得益于自己的編碼過(guò)程,另外一個(gè)很重要的來(lái)源就是閱讀別人寫(xiě)的源代碼。閱讀源代碼第三個(gè)好處,就是獲得一些好的思想。比如,有很多人在開(kāi)始一個(gè)軟件項(xiàng)目之前都喜歡到上去找一下,是否有人以前做過(guò)相同或者相似的軟件,如果有,則拿下來(lái)讀一讀,可以使自己對(duì)這個(gè)軟件項(xiàng)目有更多更深的認(rèn)識(shí)。我以前曾經(jīng)想找一本關(guān)于如何閱讀源代碼的書(shū)來(lái)看看,卻沒(méi)有找到。相反,倒是找到了不少分析源代碼的書(shū),比如Lin
4、ux kernel, Apache source, 等等。所以我想,為什么不自己來(lái)寫(xiě)下一些經(jīng)驗(yàn)和大家交流呢?(當(dāng)然不是寫(xiě)書(shū),沒(méi)有那個(gè)能力也沒(méi)有那個(gè)時(shí)間。所以在這里我準(zhǔn)備用一個(gè)例子來(lái)寫(xiě)一下如何閱讀源代碼,分享一些經(jīng)驗(yàn),算是拋磚引玉吧!我找的例子是一個(gè)統(tǒng)計(jì)日志的工具,webalizer. (這個(gè)工具我以前用過(guò),似乎記得以前的版本是用perl 寫(xiě)的,不知道現(xiàn)在為什么作者把它完全修改成了C,可能是為了效率,也可能根本就是我記錯(cuò)了。之所以選擇這個(gè)軟件來(lái)作為例子,一方面是因?yàn)樗怯肅寫(xiě)的,流程比較簡(jiǎn)單,沒(méi)有C+的程序那么多的枝節(jié),而且軟件功能不算復(fù)雜,代碼規(guī)模不大,能夠在一篇文章的篇幅里面講完; 另外一個(gè)
5、方面是因?yàn)榍∏汕岸螘r(shí)間我因?yàn)楣ぷ鞯年P(guān)系把它拿來(lái)修改了一下,剛看過(guò),還沒(méi)有忘記。:-我采用的例子是webalizer2.01-09, 也可以到它的網(wǎng)站下載最新的版本。這是一個(gè)用C寫(xiě)的,處理文本文件(簡(jiǎn)單的說(shuō)是這樣,實(shí)際上它支持三種日志文本格式: CLF, FTP, SQUID, 并且用html的方式輸出結(jié)果。讀者可以自己去下載它的源代碼包,并一邊讀文章,一邊看程序。解壓縮它的tar包(我download的是它的源代碼tar包,在文件目錄中看到這樣的結(jié)果: $ 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(這是很重要的一個(gè)環(huán)節(jié), 大體了解了軟件的功能,歷史狀況,修改日志,安裝方法等等。然后是安裝并且按照說(shuō)明中的缺省方式來(lái)運(yùn)行它,看看它的輸出結(jié)果。(安裝比較簡(jiǎn)單,因?yàn)樗鼛Я艘粋€(gè)configure, 在沒(méi)有特殊情況出現(xiàn)的時(shí)候,簡(jiǎn)單的./configure, make, make install 就可以安裝好。然后就是閱讀源代碼了。我從makefile開(kāi)始入手(我覺(jué)得這是了解一個(gè)軟件的最好的方法在makefile開(kāi)頭,有這些內(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í)行程序的安裝路徑,編譯器,配置文件的安裝路徑,編譯的選項(xiàng),安裝程序,安裝程序的選項(xiàng)等等。要注意的是,這些并不是軟件的作者寫(xiě)的,而是./configure的輸出結(jié)果。呵呵. :-下面才是主題內(nèi)容,也是我們關(guān)心的。# 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)足夠了。從這里我們可以看到這個(gè)軟件的幾個(gè)源代碼文件和他們的結(jié)構(gòu)。webalizer.c是主程序所在的文件,其他的是一些輔助程序模塊。對(duì)比一下目錄里面的文件, $ 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開(kāi)始吧。作為一個(gè)C程序,在頭文件里面,和C文件里面定義的extern變量,結(jié)構(gòu)等等肯定不會(huì)少,但是,單獨(dú)看這些東西我們不可能對(duì)這個(gè)程序有什么認(rèn)識(shí)。所以,從main函數(shù)入手,逐步分析,在需要的時(shí)候再回頭來(lái)看這些數(shù)據(jù)結(jié)構(gòu)定義才是好的方法。(順便說(shuō)一句,Visual C+, 等windows下的IDE工具提供了很方便的方法來(lái)獲取函數(shù)列表,C
15、+的類列表以及資源文件,對(duì)于閱讀源代碼很有幫助。Unix/Linux 也有這些工具,但是,我們?cè)谶@里暫時(shí)不說(shuō),而只是通過(guò)最簡(jiǎn)單的文本編輯器vi來(lái)講。跳過(guò)webalizer.c 開(kāi)頭的版權(quán)說(shuō)明部分(GPL的,和數(shù)據(jù)結(jié)構(gòu)定義,全局變量聲明部分,直接進(jìn)入main(函數(shù)。在函數(shù)開(kāi)頭,我們看到:/* initalize epoch */epoch=jdate(1,1,1970; /* used for timestamp adj. */* add default index. alias */add_nlist(index.,&index_alias;這兩個(gè)函數(shù)暫時(shí)不用仔細(xì)看,后面會(huì)提到,略過(guò)。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;從注釋和程序本身可以看出,這是查找是否存在一個(gè)叫做webalizer.conf的配置文件,如果當(dāng)前目錄下有,則用get_config來(lái)讀入其中內(nèi)容,如果沒(méi)有,則查找ETCDIR/webalizer.conf是否存在。如果都沒(méi)有,則進(jìn)入下一部分。(
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ù)及開(kāi)關(guān)。(getopt(的用法我在另外一篇文章中講過(guò),這里就不再重復(fù)了。可以看到,這個(gè)軟件雖然功能不太復(fù)雜,但是開(kāi)關(guān)選項(xiàng)還是不少。大多數(shù)的unix/linux程序的開(kāi)頭部分都是這個(gè)套路,初始化配置文件,并且讀入分析命令行。在這段程序中,我們需要注意一個(gè)函數(shù):add_
26、nlist(. print_opts(, get_config(等等一看就明白,就不用多講了。這里我們已經(jīng)是第二次遇到add_nlist這個(gè)函數(shù)了,就仔細(xì)看看吧。$ grep add_nlist *.hlinklist.h:extern int add_nlist(char *, NLISTPTR *;/* add list item */可以發(fā)現(xiàn)它定義在linklist.h中。在這個(gè)h文件中,當(dāng)然會(huì)有一些數(shù)據(jù)結(jié)構(gòu)的定義,比如: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;這是兩個(gè)鏈表結(jié)構(gòu)。還有extern GLISTPTR group_sites ; /* group lists */extern GLISTPTR group_urls ;extern GLISTPTR group_refs ;這些都是鏈表,太多了,不用一一看得很仔細(xì),因?yàn)槟壳耙部床怀鰜?lái)什么東西。
28、當(dāng)然要注意它們是extern的,也就是說(shuō),可以在其他地方(文件看到它們的數(shù)值(類似于C+中的public變量。這里還定義了4個(gè)函數(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的,也就是說(shuō),可以在其他地方見(jiàn)到它們的調(diào)用(有點(diǎn)相當(dāng)于C+中的public 函數(shù)。再來(lái)看看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個(gè)函數(shù)是內(nèi)部使用的(相當(dāng)于C+中的private, 也就是說(shuō),這些函數(shù)只被isinlist(NLISTPTR, char *, isinglist(GLISTPTR, char *, add_nlist(char *, NLISTPTR *, add_glist(char *, GLISTPTR *調(diào)用,而不會(huì)出現(xiàn)在其他地方。所以,我們先來(lái)看這幾個(gè)內(nèi)部函數(shù)。舉例來(lái)說(shuō),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;這個(gè)函數(shù)分配了一個(gè)struct nlist, 并且把其中的string賦值為str, next賦值為NULL.這實(shí)際上是創(chuàng)建了鏈表中的一個(gè)節(jié)點(diǎn)。verbose是一個(gè)全局變量,定義了輸出信息的類型,如果verbose為1,則輸出很詳細(xì)的信息,否則輸出簡(jiǎn)略信息。這是為了調(diào)試或者使用者詳細(xì)了解程序情況來(lái)用的。不是重要內(nèi)容,雖然我們常??梢栽谶@個(gè)源程序的其他地方看到它。另外一個(gè)函數(shù):void del_nlist(NLISTPTR *listNLISTPTR cptr,nptr;cptr=*list;while (cp
32、tr!=NULLnptr=cptr-next;free(cptr;cptr=nptr;這個(gè)函數(shù)刪除了一個(gè)nlist(也可能是list所指向的那一個(gè)部分開(kāi)始知道鏈表結(jié)尾,比較簡(jiǎn)單。看完了這兩個(gè)內(nèi)部函數(shù),可以來(lái)看/*/* 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;這個(gè)函數(shù)是建立了一個(gè)新的節(jié)點(diǎn),把參數(shù)str賦值給新節(jié)點(diǎn)的string, 并把它連接到list所指向鏈表的結(jié)尾。另外的三個(gè)函數(shù):new_glist(, del_glist(, add_glist(完成的功能和上述三個(gè)差不多,所不同的只是它們所處理的數(shù)據(jù)結(jié)構(gòu)不同??赐炅诉@幾個(gè)函數(shù),我們回到main程序。接下來(lái)是,/* setup our internal variables */init_counters(; /* inital
34、ize main counters */我們所閱讀的這個(gè)軟件是用來(lái)分析日志并且做出統(tǒng)計(jì)的,那么這個(gè)函數(shù)的名字已經(jīng)告訴了我們,這是一個(gè)初始化計(jì)數(shù)器的函數(shù)。簡(jiǎn)略的看看吧!$ grep init_counters *.hwebalizer.h:extern void init_counters(;在webalizer.c中找到:void init_counters(int i;for (i=0;i根據(jù)在最開(kāi)始讀過(guò)的README文件,這個(gè)page_type是用來(lái)定義處理的頁(yè)面的類型的。在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!我們?cè)诔绦蛑幸部梢钥吹?如果沒(méi)有在命令行中或者config文件中指定,則根據(jù)處理的日志文件的類型來(lái)添加缺省的文件類型。比如對(duì)于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;.這一段是對(duì)于FTP的日志格式,設(shè)置搜索列表。for (i=0;i清空哈西表,為下面即將進(jìn)行的排序工作做好準(zhǔn)備。關(guān)于哈西表,這是數(shù)據(jù)結(jié)構(gòu)中常用的一種用來(lái)快速排序的結(jié)構(gòu),如果不清楚,可以參考相關(guān)書(shū)籍,比如清華的教材或者等書(shū)。if (verbose1uname(&system_info;printf(Webalizer V%s-%s (%s %s %s ,version,editlvl,system_info.sysname,system_info.relea
37、se,language;這一段,是打印有關(guān)系統(tǒng)的信息和webalizer程序的信息(可以參考uname的函數(shù)說(shuō)明。#ifndef USE_DNSif (strstr(argv0,webazolver!=0printf(DNS support not present, aborting. ;exit(1;#endif /* USE_DNS */這一段,回憶我們?cè)诳碦EADME文件的時(shí)候,曾經(jīng)提到過(guò)可以在編譯的時(shí)候設(shè)置選項(xiàng)開(kāi)關(guān)來(lái)設(shè)定DNS 支持,在源代碼中可以看到多次這樣的代碼段出現(xiàn),如果不指定DNS支持,這些代碼段則會(huì)出現(xiàn)(ifdef或者不出現(xiàn)(ifndef.下面略過(guò)這些代碼段,不再重復(fù)。/*
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)讀到過(guò),如果log文件是gzip壓縮格式,則用gzopen函數(shù)打開(kāi)(可以猜想gz*是一套針對(duì)gzip壓縮格式的實(shí)時(shí)解壓縮函數(shù),如果不是,則用fopen打開(kāi)。/* 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文件中讀到過(guò),如果參數(shù)行有-o out_dir, 則將輸出結(jié)果
40、到該目錄,否則,則輸出到當(dāng)前目錄。在這一段中,如果輸出目錄不存在(chdir(out_dir != 0則出錯(cuò)。#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)提到過(guò)
41、,這是DNS解析的代碼部分,可以略過(guò)不看,不會(huì)影響對(duì)整個(gè)程序的理解。/* prep hostname */if (!hnameif (uname(&system_info hname=localhost;else hname=system_info.nodename;這一段繼續(xù)處理參數(shù)做準(zhǔn)備工作。如果在命令行中指定了hostname(機(jī)器名則采用指定的名稱,否則調(diào)用uname查找機(jī)器名,如果沒(méi)有,則用localhost來(lái)作為機(jī)器名。(同樣在README中說(shuō)得很詳細(xì)/* get past history */if (ignore_hist if (verbose1 printf(%s ,msg_
42、ign_hist; else get_history(;如果在命令行中指定了忽略歷史文件,則不讀取歷史文件,否則調(diào)用get_history(來(lái)讀取歷史數(shù)據(jù)。在這里,我們可以回想在README文件中同樣說(shuō)過(guò)這一細(xì)節(jié),在命令行或者配置文件中都能指定這一開(kāi)關(guān)。需要說(shuō)明的是,我們?cè)谶@里并不一定需要去看get_history這一函數(shù),因?yàn)閺暮瘮?shù)的名稱,README文件和程序注釋都能很清楚的得知這一函數(shù)的功能,不一定要去看代碼。而如果要猜想的話,也可以想到, history是webalizer在上次運(yùn)行的時(shí)候記錄下來(lái)的一個(gè)文件,而這個(gè)文件則是去讀取它,并將它的數(shù)據(jù)包括到這次的分析中去。不信,我們可以來(lái)看看
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中,這兩個(gè)函數(shù)是成對(duì)出現(xiàn)的。get_history(讀取文件中的數(shù)據(jù),并將其記錄到hist_開(kāi)頭的
46、一些數(shù)組中去。而put_history(則是將一些數(shù)據(jù)記錄到同樣的數(shù)組中去。我們可以推測(cè)得知,hist_數(shù)組是全局變量(在函數(shù)中沒(méi)有定義,也可以查找源代碼驗(yàn)證。同樣,我們可以找一找put_history(出現(xiàn)的地方,來(lái)驗(yàn)證剛才的推測(cè)是否正確。在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 */可以知道,推測(cè)是正確的。再往下讀代碼,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ù)準(zhǔn)備,而且和get_history(, put_history(有些類似,讀者可以自己練習(xí)一下。下面,終于進(jìn)入了程序的主體部分, 在做完了命令行分析,數(shù)據(jù)準(zhǔn)備之后,開(kāi)始從日志文件中讀取數(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我看到這里的時(shí)候,頗有一些不同意作者的這種寫(xiě)法。這
49、一段while中的部分寫(xiě)的比較復(fù)雜而且效率不高。因?yàn)閺某绦蛲茢嗪蛷乃拇a看來(lái),作者是想根據(jù)日志文件的類型不同來(lái)采用不同的方法讀取文件,如果是gzip格式,則用our_gzgets來(lái)讀取其中一行,如果是普通的文本文件格式,則用fgets(來(lái)讀取。但是,這段代碼是寫(xiě)在while循環(huán)中的,每次讀取一行就要重復(fù)判斷一次,明顯是多余的而且降低了程序的性能??梢栽趙hile循環(huán)之前做一次這樣的判斷,然后就不用重復(fù)了。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這一段代碼,讀入一行,如果這一行超過(guò)了程序允許的最大字符數(shù)(則是錯(cuò)誤的日志數(shù)據(jù)紀(jì)錄,則跳過(guò)本行剩下的
51、數(shù)據(jù),忽略掉(continue進(jìn)行下一次循環(huán)。同時(shí)把total_bad增加一個(gè)。如果沒(méi)有超過(guò)程序允許的最大字符數(shù)(則是正確的日志數(shù)據(jù)紀(jì)錄,則/* got a record. */strcpy(tmp_buf, buffer; /* save buffer in case of error */if (parse_record(buffer /* parse the record */將該數(shù)據(jù)拷貝到一個(gè)緩沖區(qū)中,然后調(diào)用parse_record(進(jìn)行處理。我們可以同樣的推測(cè)一下, get_record(是這個(gè)程序的一個(gè)主要處理部分,分析了日志數(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是一個(gè)全局變量,該函數(shù)根據(jù)日志文件的類型,分別調(diào)用三種不同的分析函數(shù)。在webalizer.h中,找到該變量的定義,從結(jié)構(gòu)定義中可以看到,結(jié)構(gòu)定義了一個(gè)日志文件所可能包含的所有信息(參考CLF,FTP, SQUID日志文件的格式說(shuō)明。/* log record structure */struct log_struct c
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024-2025學(xué)年新教材高中數(shù)學(xué) 第八章 立體幾何初步 8.4.2 空間點(diǎn)、直線、平面之間的位置關(guān)系(教學(xué)用書(shū))教學(xué)實(shí)錄 新人教A版必修第二冊(cè)
- 2025年二級(jí)井下采煤機(jī)司機(jī)(技師)技能認(rèn)定理論考試題(附答案)
- 施工員個(gè)人年度工作總結(jié)
- 整形醫(yī)院護(hù)士試用期個(gè)人總結(jié)
- 2025年商業(yè)房產(chǎn)貸款借款合同書(shū)(合同版本)
- 電子設(shè)備采購(gòu)合同范本
- 演員表演服務(wù)地點(diǎn)合同
- 獸藥購(gòu)買(mǎi)核對(duì)合同標(biāo)準(zhǔn)文本
- 代理采購(gòu)電器合同標(biāo)準(zhǔn)文本
- 2025購(gòu)銷合同詳細(xì)版
- 2025屆成都市2022級(jí)高中畢業(yè)班第二次診斷性檢測(cè)語(yǔ)文試題及答案
- 2025屆北京市第四中學(xué)順義分校高三零模英語(yǔ)試題(原卷版+解析版)
- 全國(guó)第9個(gè)近視防控月活動(dòng)總結(jié)
- 智能傳感器研發(fā)-第1篇-深度研究
- 2025至2030年中國(guó)快速換模系統(tǒng)數(shù)據(jù)監(jiān)測(cè)研究報(bào)告
- 2025年舉辦科普月的活動(dòng)總結(jié)(3篇)
- 2025年高三語(yǔ)文上學(xué)期期末考試作文題目解析及范文:關(guān)于鴻溝的思考
- 2025年春新人教版化學(xué)九年級(jí)下冊(cè)課件 第十一單元 化學(xué)與社會(huì) 課題1 化學(xué)與人體健康
- 光的折射(課堂PPT)
- 監(jiān)控系統(tǒng)維護(hù)及方案
- 無(wú)心磨床新手
評(píng)論
0/150
提交評(píng)論