From: IBM:網雲運算為資料中心終極目標
所謂網雲運算係指由遠端網際網路上的伺服器群進行資料的存取與運算。這些伺服器群可能分散在各處不同的資料中心,同時處理來自各地成千上萬使用者的需求,從使用者的觀點來看,根本無從分辨是哪一台伺服器處理了自己送出的運算需求。
我的外部記憶區
昨晚我參加一個的安全防範的研習會,讓我很驚嘆!這場研習會是由一個叫Pat Malone 的人主持,他是知名人士的保鑣,也為FBI工作,並教授警察及海軍的SEALS徒手格鬥。
TIPS FROM USA POLICE
This is a good reminder for all of us. You can never Read this too many times!
以下建議非常實用,多看幾次,受用無窮!
一個叫泰得‧邦迪(Ted Bundy)的連續殺人犯就是一個相貌堂堂並且受過良好教育的人,總是利用女性的同情心。他走路時帶著一根手杖或是跛行,經常要求別人「幫忙」他進入車內或是看一下他的車子,趁機綁架受害者。
最近有人告訴我,他的朋友在晚上聽到門口有嬰兒在哭,不過當時已經很晚了,而且她認為這件事很奇怪,於是她打電話給警察。警察告訴她:「無
論如何,絕對不要開門。」這位女士表示那聲音聽起來像是嬰兒爬到窗戶附近哭,她擔心嬰兒會爬到街上,被車子碾過。警察告訴她:我們已經派人前往,無論如何
不能開門。警方認為這是一
個連續殺人犯,利用嬰兒哭聲的錄音帶,誘使女性以為有人在外面遺棄嬰兒,騙她們出門察看。雖然尚未證實此事,但是警方已經接到許多女性打電話來說,他們晚
上獨自在家時,聽到門外有嬰兒的哭聲,請將這個消息傳給其他人,不要因為聽到嬰兒的哭聲而開門。
請嚴肅看待這封電子郵件,因為上禮拜六「美國頭號通緝犯」(America’s Most Wanted)節目中,報導路易斯安那州的連續殺人犯時,提到了「哭泣嬰兒」的假設。
請轉寄這封信給你所有認識的女性朋友,並一個接一個傳下去。我原本只想把這封信轉寄給女性朋友,但是男性朋友,假如你愛你的媽媽、太太、姊妹、女兒等等,你也應該把這個訊息傳給他們。
From: 豬扒
Newsgroups: mnb.personal.pigpig
5月25日
美國斯坦福大學科學家使用粒子加速器發射出來X光,掃瞄一本800年前遭基督教僧侶刮除重寫的
「阿基米德羊皮書」,從而使這本書的原文再見天日,不過這本書共計有174頁的羊皮紙,全部工程將耗時3到4年才能完成。
這本「阿基米德羊皮書」,不是阿基米德的原版書,而是公元10世紀左右,人們抄寫的阿基米德科學著作。這本羊皮書是由一名私人收藏家在1998年花了200萬美元買到的,他交給美國馬里蘭州巴爾的摩市的「沃特斯藝術博物館」保存。
該館珍稀古籍手稿保管專家阿比蓋爾.庫恩特表示,這本阿基米德遺稿很可能包含了近代科學家殫心竭慮幾世紀都沒有發現的東西。不過由於這本羊皮書共有174頁,要想把這本書的字跡全部再現出來,恐怕需要3年到4年的時間。
庫恩特說,大約800年前,這本羊皮紙遺稿被一名中世紀修道院的僧侶所保管,當時這名僧侶在寫祈禱書時,竟然拿出了修道院收藏的阿基米德遺稿,一頁頁洗去
上面的墨水,然後在羊皮紙上抄寫祈禱書。幸運的是,這名僧侶當時並沒有完全洗盡遺稿上的字跡,羊皮紙上還留著一些淡淡的痕跡。
那時人們用的還是所謂的「羊皮紙」,即是羊的皮剝下來,分成薄薄的幾層,經過乾燥等處理後可以在上面寫字,因此紙張十分珍貴。這名修道士使用由浮石粉和檸檬水混合製成的「墨水」,在價值連城的「阿基米德羊皮書」上重新寫了祈禱文。
多年來科學家一直想辦法企圖再現這本書的原文。斯坦福大學的物理學家柏爾格曼發現,在抄寫阿基米德原文使用的「墨水」中,含有鐵的
粒子,並發現使用粒子加速器,可以偵測出墨水中少量的鐵質。通過電子在圓形加速器中加速到近光速所釋出的X光照射,可以使墨水中的鐵分子發出螢光從而顯現
出來,這樣也就會露出原來的字跡。
當阿基米德遺稿中的內容被復原出一部分後,科學家們被遺稿內容驚得目瞪口呆。令科學家們難以置信的是,阿基米德在一篇名叫《機械
定理方法》的文章中,竟然頗費筆墨地闡述了現代微積分學理論的精粹!顯然阿基米德當時對這一領域已有了至關緊要的發現。另外,復原的內容還包括了阿基米德
的《浮體原理》的理論,裡面講述的就是著名的浮力原理。
華特美術館「阿基米德重寫本專案」主任諾埃爾表示,最令人喜悅與期待的事是大家不曉得會揭露出什麼文章。
要產生 des 加密格式的密碼使用如下:
openssl passwd -crypt "your_password"
若是要產生 md5-based 的加密格式密碼,那使用則是:
openssl passwd -1 "your_password"
其中通常不使用 -crypt 預設就是產生 des 的格式,所以看情況可以省略使用。再來就是可以傳入 -salt 指定使用的 “salt” 項目。比方使用像是:
openssl passwd -crypt -salt "AB" "your_password"
openssl passwd -1 -salt "ABCDEFGHI" "your_password"
要注意 des 的 slat 限制兩個字元,範圍是 [a–zA–Z0–9./]。若是使用 md5 的話要傳入 8 個字元,內容範圍是[a–zA–Z0–9./]。
總幹事趙煥明呼籲香港市民伸出援手,並公佈宣明會的「賑災戶口」為
匯豐銀行:018-554444-001;
恒生銀行:286-364385-003;
中國銀行 (香港):012-883-0-002666-2。
查詢及捐款熱線(+852)23942394,
電郵:hotline@worldvision.org.hk。
來源 : hk.news.yahoo.com
香港紅十字會
在多家銀行設立中國賑災基金戶口,
匯豐銀行:567-650-155-016;
恒生銀行:267-175-123001;
中國銀行:806-0000-1617;
東亞銀行:514-40399-663。
來源 : www.mpinews.com
樂施會捐款方法
1.捐款戶口:
匯豐銀行:001-537000-012
中國銀行:012-874-0010515-7
2.郵寄支票:
支票抬頭請寫「樂施會」,支票背面註明「內地地震」。
3.「繳費靈」(PPS):
「繳費靈」用戶可致電18033或登入www.ppshk.com 捐款。請輸入樂施會的商戶編號「9245」,並選擇賬單類別「04」樂施會緊急救援項目
救世軍捐款方法
1.全港「7.11」便利店
2.捐款戶口:
匯豐銀行:580-149649-001
中國銀行:012-878-1-061887-6
東亞銀行:015-515-10-50541-3
3.郵寄支票:
捐款者可郵寄支票到「九龍油麻地永星里11號救世軍港澳軍區總部」,支票抬頭請註明「救世軍」,背書「四川地震」。
捐款查詢熱線:2783-2333
如果房間外都是煙和火而逃不出去的話,這時房間內如果有馬桶,可以把臉埋入馬桶吸取僅存的空氣(意思就是告誡大家平常要多洗馬桶 )。如果沒有馬桶 .... 通常衣櫥因為都是關著的,所以衣櫥內也有剩餘的空氣,可以躲在衣櫥內,不管躲在哪裡都要用硬物敲擊牆壁或地面讓救生員可以找到你…
火場逃生採低姿勢爬行逃出,但如果是在公共場所遇到火災就不要再爬了,因為可能沒被燒死先被 踩死…當然如果可以的話,沿著牆壁採低姿勢快速逃出最好…
在火場內要不要睜開眼睛??
逃生時需不需要用塑膠袋裝空氣套在頭上??
愛美的女士遇到火災第一件事是什麼??
一般的公共場所會有防煙面罩,但是材質都是玻璃紙的那種,當然又和塑膠袋沒兩樣,有一種比較好的防煙面罩,不僅防煙還防火,而且長達15分鐘,(我們研習的時候當場有請一位老師上去示範,帶著面罩用火直接燒都沒感覺)最好家裡可以購置……
記得請大家告訴大家正確的消防觀念…
光是攝影的重要手段和特徵,所以要學會用光來表達自己的主題。順光畫面平淡;側光立體感強;逆光層次豐富。另外,要善於運用色彩來突出你的攝影主題。在旅
遊中我們經常置身於大自然中,「萬綠叢中一點紅」,就是用色的一個原則,使主體更易於突出。懂得並巧妙地使用濾色鏡或效果鏡,能夠使畫面的色彩還原正確和
達到自己的預定要求,如使用偏振鏡,在風光攝影中能反差成暗色的天空,使藍天的色彩更為飽和,白雲更白。使用橙色漸變鏡能更加渲染日出或日落時的氣氛和色
彩。
總幹事趙煥明呼籲香港市民伸出援手,並公佈宣明會的「賑災戶口」為
匯豐銀行:018-554444-001;
恒生銀行:286-364385-003;
中國銀行 (香港):012-883-0-002666-2。
查詢及捐款熱線(+852)23942394,
電郵:hotline@worldvision.org.hk。
來源 : hk.news.yahoo.com
香港紅十字會
在多家銀行設立中國賑災基金戶口,
匯豐銀行:567-650-155-016;
恒生銀行:267-175-123001;
中國銀行:806-0000-1617;
東亞銀行:514-40399-663。
來源 : www.mpinews.com
樂施會捐款方法
1.捐款戶口:
匯豐銀行:001-537000-012
中國銀行:012-874-0010515-7
2.郵寄支票:
支票抬頭請寫「樂施會」,支票背面註明「內地地震」。
3.「繳費靈」(PPS):
「繳費靈」用戶可致電18033或登入www.ppshk.com 捐款。請輸入樂施會的商戶編號「9245」,並選擇賬單類別「04」樂施會緊急救援項目
救世軍捐款方法
1.全港「7.11」便利店
2.捐款戶口:
匯豐銀行:580-149649-001
中國銀行:012-878-1-061887-6
東亞銀行:015-515-10-50541-3
3.郵寄支票:
捐款者可郵寄支票到「九龍油麻地永星里11號救世軍港澳軍區總部」,支票抬頭請註明「救世軍」,背書「四川地震」。
捐款查詢熱線:2783-2333
Function: 針對某一Web Server提出連線請求,並且在同一時間內可設定幾個連線請求
Usage: # ab -n N -c N http://server1.example.com/index.html
-c concurrency
Number of multiple requests to perform at a time. Default is one request at a time.
-n requests
Number of requests to perform for the benchmarking session. The default is to just perform
single request which usually leads to non-representative benchmarking results.
Why on earth build a relay computer when there are already computers over a hundred thousand times faster? Well, for two reasons: to prove that silicon is not magic and because I want to.
wc files —— 統計文件中的行數,字數和字符數。 pr files —— 打印文件,支持標題和多欄打印。 lpr files —— 打印文件 grep pattern files —— 找到符合某種模式的文件行。許多程序員的工作就是用它們和一些其他相關程序完成的。例如:
wc *.c用於對所有C源代碼文件進行代碼量統計;
grep goto *.c用於找到所有的goto語句。 這些就是「最為有用的」?!?! 有道理。這就是程序員的日常工作。事實上,今天我就用了不少時間來統計我的C代碼量,以至於沒有多少時間去做其他事情。等一下,我想我還得再數一遍。 同一期《IEEE計算機》上還有一篇文章,是Warren Teitelman和Larry Masinter寫的《Interlisp編程環境》.Interlisp是個極為複雜的編程環境。1981年Interlisp就有了Unix程序員到了1984還在夢想的工具。 Interlisp環境的設計者們使用的是完全不同的方法。他們決定開發一個複雜的工具,需要花不少時間來掌握,好處是一旦學會了,極大地提高編程效率。聽上去有些道理。 悲哀的是,今天很少有程序員能體會使用這類環境的感覺了。 ==在柏拉圖的洞穴裡編程== 我總有一種感覺,計算機語言設計和工具開發的目標應該是提高編程效率而不是降低。 comp.lang.c++上的一個貼子 ---- 計算機以外的其他產業早就體會到了自動化的意義。當人們走進快餐點,他們需要的是一致標準的東西,而不是什麼法國大菜。大規模地提供一致的一般食物,這比小批量的精耕細作要賺錢得多。 ---- netnews上一個技術人員的回復 ---- Unix不是世界上最好的軟件環境——它甚至不是一個好的環境。Unix編程工具又簡陋又難用;Unix調試器和PC上的沒法比;解析器(interpreters)仍然是富人的玩具;修改日誌(change log)和審記(audit trail)總是想起來才去做。可 Unix仍然被當成程序員的夢。也許它只能讓程序員夢到了效率的提高,而不是真的提高效率。 Unix程序員有點像數學家。你能從他們身上觀察到一個神秘現象,我們稱之為「空頭編程」(Programming by Implication)。一次我們和一個Unix程序員聊天,談到需要這樣一個 工具,能夠回答諸如「函數foo被誰調用過?」或者「那個函數改變過全局變量bar」之類的問題。他也認為這個工具會很有用,提議到,「你們可以自己寫一個。」 公平地說,他之所以只是說「你們可以自己寫一個」而不是真正寫一個,這是因為C語言的一些特性和Unix「編程環境」的強強聯手,使得寫這樣的程序難於上青天。 使用yacc進行解析(parsing with yacc) "Yacc"就是我用過yacc(1)之後想喊的。 ——匿名 ---- "YACC"是再一個編譯編譯器的編譯器(Yet Another Compiler Compiler)的意思。它接受與上下文無關(context-free)的語法,構造用於解析的下推自動機(pushdown automaton)。運行這個自動機,就得到了一個特定語言的 解 析器。這一理論是很成熟的,因為以前計算機科學的一個重要課題就是如何減少編寫編譯器的時間。 這個方法有個小問題:許多語言的語法不是與上下文無關的。這樣yacc的使用者不得不在每一個狀態轉換點上加上相關代碼,以處理和上下文有關的部分(類型檢查一般就是這麼處理的)。許多C編譯器使用的都是yacc生成的解析器;GCC 2.1的yacc語法 有1650行之多 (如果不用yacc,GCC應該能成為自由軟件基金會不錯的作品)。由yacc生成的代碼就更多了。 有些編程語言的語法比較容易解析。比如,Lisp能夠用一個遞歸下降解析器進行解析。「遞歸下降」是一個計算機術語,含義是「喝杯可樂的功夫就能實現」。作為試驗,我們寫了一個Lisp遞歸下降解析器,只用了250行C代碼。如果是用Lisp寫的,那麼一頁紙也用不了。 在上面提到的那個計算機科學原始時代,這本書的編輯還沒有生出來呢。計算機房是恐龍的天下,「真正的人」都在用儀表盤上的開關來編程。今天,社會學家和歷史工作者想破腦袋也無法理解為什麼理智的程序員卻設計、實現和傳播了如此難解析的語言。也許他們那時候極需一個困難的研究項目,設計一個難於解析的語言似乎是個不錯的課題。 一直想知道他們在那個時代吃的是什麼藥。 上面提到的那個工具類似於一個C編譯器的前端。C編譯器前端是個極其複雜的東西,這是C的複雜語法和yacc的使用造成的。沒有人真正動手去寫一個這樣的工具,這還有什麼奇怪的麼? 死硬的Unix分子會說你不需要這麼一個程序,因為有grep就足夠了。而且,你還能在shell管道中使用grep。有一天,我們想找出BSD內核源碼中所有使用min函數的地方。這是其中一個結果:
% grep min netinet/ip_icmp.c icmplen = oiplen + min(8, oip->ip_len); * that not corrupted and of at least minimum length. * If the incoming packet was addressed directly to us, * to the incoming interface. * Retrieve any source routing from the incoming packet; %挺不錯的吧,grep找到了所有的min函數調用,而且還不止這些。 「不知道怎麼做愛。我撤。」("Don't know how to make love. Stop.") 理想的編程工具應該是這樣的,它能讓簡單的問題保持簡單,讓複雜的問題有解決的可能。不幸的是,許多Unix工具過分追求通用性,而忽視了簡潔。 Make就是這樣一個典型。從抽像意義而言,make的輸入是一個倚賴關係的描述。倚賴圖上的每個節點都對應這一組命令,當節點過期時(由它所倚賴的節點來決定),這些命令會被執行。節點和文件相關,文件的修改時間決定了節點是否過期。下面是一個簡單的倚賴關係圖,也就是Makefile:
program: source1.o source2.o cc -o program source1.o source2.o source1.o: source1.c cc -c source1.c source2.o: source2.c cc -c source2.c這裡program, source1.o, source2.o, source1.c,source2.c就是關係圖上的節點。節點program倚賴於source1.o和source2.o。 如果source1.o或source2.o比program要新,make便會運行命令cc -o program source1.o source2.o重新生成program。當然,如果修改了source1.c,那麼source1.o和program都會過時,所以make會重新進行編譯和鏈接。 儘管make的模型很通用,可惜設計者從沒有考慮過簡單性。不過,許多Unix新手都能體會到make能多麼簡單地「鑽」(screw)了他們。 繼續我們上面的那個例子,假定有個程序員Dennis想調試source1.c,於是要編譯使用調試選項。他修改了一下Makefile:
program: source1.o source2.o cc -o program source1.o source2.o # I'm debugging source1.c source1.o: source1.c cc -c source1.c source2.o: source2.c cc -c source2.c"#"打頭的那行是註釋,會被make忽略。可憐的Dennis運行了一下make,這是它得到的:
Make: Makefile: Must be a speparator on line 4. Stopmake歇菜了。Dennis盯著Makefile看了有好幾分鐘,又看了幾小時,還是不明白哪兒出錯了。他覺得是註釋行的問題,可不是很肯定。 毛病出在當他加入註釋行時,他不小心在第二行開始的製表符(tab)前敲入了一個空格。製表符是Makefile語法的一個重要部分。所有的命令行(例子中cc開始的行)必須以製表符打頭。這就是Dennis的Makefile不工作的原因。 「那又怎樣?」你可能會說,「這有什麼不對的?」 它本身沒什麼不對。不過如果你想一下其他Unix編程工具的工作方式,就會覺得製表符語法就好像《地雷戰》裡的頭髮絲雷,看上去一馬平川,踩上去嗚呼哀哉。 你知道,製表符、空格符和換行符一般被統稱為「白字符」(whitespacecharacters)。「白字符」意味著「你可以放心大膽地忽略它」許多程序正是這麼做的,對空格和製表符一視同仁。就make孤芳自賞桀驁不馴鶴立雞群冰清玉潔眾人皆醉唯我獨醒。於是我們這位Dennis兄弟恐怕只能給自己腦袋來一槍,告別這悲慘的Unix世界。 可憐的Dennis最終也沒有找到自己那個Makefile的毛病,他現在落魄到只好去給一個中西部州立大學維護sendmail配置文件。默哀三分鐘。 ==頭文件== C語言有個東西叫頭文件,裡面是一些說明信息,在編譯時被源文件使用。和Unix上的其他玩意一樣,如果只有一個兩個,可以工作得很好,多了就沒戲了。 要知道你的源文件該使用那個頭文件,這可不是件容易事。頭文件是C預處理器(preprocessor)根據#include指令(directive)加載的。這個指令有兩個用法:
#include和
#include "header2.h"這兩種用法的區別和各個C預處理器的實現有關,也就是說,任何實現都可以大著膽子撒著歡兒由著性子亂來。 讓我們來看看Dennis的朋友Joey,Joey也是個Unix新手。Joey有個C程序foo.c,使用了foo.h中定義的一些數據結構,foo.c和foo.h放在了同一個目錄下。你可能已經知道"foo"是程序員常用的名字。Joey機器上的系統程序員也做了一個foo.h文件,並把它放到了缺省系統頭文件目錄/usr/include 倒霉蛋Joey編譯了foo.c,得到一堆語法錯誤。他迷惑不解,編譯器總在他定義的一些數據結構處報錯,可是這些數據結構在foo.h裡被定義的好好的呀。 你我估計能猜到Joey的問題在哪兒,他一定是這麼加載頭文件的:
#include而不是寫成:
#include "foo.h"可Joey不知道這個。也可能他確實是用的引號方式,只是他的編譯器的查找方式有些特別。不管怎樣,Joey是被幹掉了,很無辜地被干了。 維護很多頭文件是件挺頭疼的事,不幸的是,如果你寫個有用點兒的C程序,這是不可避免的。頭文件一般 於定義數據結構,一個頭文件往往倚賴於其他一?頭文件。去把那些頭文件 的倚賴關係整理一下,你這回可不愁沒事兒做了。 當然,編譯器會幫你的。如果你把倚賴關係搞錯了,編譯器會毫不留情地指出語法錯誤。記住,編譯器是個很忙很有身份的程序,它沒時間去區分未定義的數據結構和輸入錯誤的區別。事實上,即使你只是忘了敲個分號,C編譯器也會惱羞成怒,立馬撂挑子不幹了。 在編譯器社區,這一現象被稱為「錯誤雪崩」,或者按照編譯器自己的說法:「我完蛋了,起不來了。」 缺個分號會把解析器徹底搞暈,狂吐不止。這個解析器很可能是用yacc寫 成 的,yacc對語法正確的程序(很少見的一種情況)處理得很好,但要讓它生成健壯容錯自動恢復的解析器,這就有點兒勉為其難了。有經驗的C程序員都知道只有第一條解析錯誤才是有意義的。 ==工具程序和Man手冊== Unix工具是自成一體的;可以任意解釋命令行參數。這樣的自由有些煩人;別以為學會了一套命令行規則就一勞永逸了,你必須去讀每個命令的Man手冊,才能知道如何去使用。 知道有那麼多清楚明白的Man手冊供你參考,你一定很開心吧。 看一下下面這個例子。「摘要」一欄總結得挺不錯的,是不是?
LS(1) Unix程序員手冊 LS(1) 名稱 ls - 列出目錄內容 摘要 ls [ -acdfgilqrstu1ACLFR ] 名稱 ... 描述 對於每個目錄參數,ls列舉那個目錄的內容;對於每個文件參數, ls 給出文件名以及要求的其他信息。缺省情況下,輸出將按照字 母順序排列。如果沒有參數,則列舉當前目錄的內容。如果有不只 一個參數,這些參數首先會被適當排序,但是文件參數總是會被排 在目錄參數前面。 ls有很多選項: [ ... ] BUGS 文件名中的換行符和製表符會被可打印字符 輸出設備會被假設有80列寬輸出會根據輸出設備的不同而不同,比如"ls -s"的結果和"ls -s| lpr"的結果不一樣。這是不正確的,然而如果不這麼做,一些倚賴這個功能的舊有shell腳本就會完蛋。 如果你想玩個遊戲,不妨讀一下每個Man手冊的BUGS部分,然後想像一下每個bug是如何造成的。看一下這個shell的man手冊:
SH(1) Unix程序員手冊 SH(1) 名稱 sh, for, case, if, while, :, ., break, continue, cd, eval, exec, exit, export, login, read, readonly, set, shift, times, trap, umask, wait - 命令語言 摘要 ls [ -ceiknrstuvx ] [參數] ... 描述 Sh是一個命令程序語言,它執行來自終端或文件的命令。下面是各 個選項的說明。 [ ... ] BUGS 如果把使用<<提供的標準的輸入提供給使用&運行起來的非同步的進程, shell會搞不清楚輸入文檔的名字。會生成一個垃圾文件/tmp/sh*, shell會抱怨找不到使用另外一個名字的文檔。我們用了好幾分鐘也沒搞明白這個bug究竟是他媽什麼意思。一個Unix專家看過之後說:「我邊看邊撓腦袋,有寫這段BUGS的功夫,估計足夠這傢伙改掉這個吊玩意了。」 不幸的是,修改bug幾乎是不可能的,因為它會隨著每個新發佈的操作系統而捲土重來。在80年代早期,在這些bug還沒有被Unix信徒奉為神聖以前,一個BBN的程序員真的修改了伯克利make的這個製表符bug。這不是很難,也就是幾行代碼的事兒。 和所有責任感的公民一樣,BBN的駭客們把補丁發給了伯克利,希望能把它加入主Unix代碼中。一年過後,伯克利發佈了新版本的Unix,make的這個bug還是存在。BBN的駭客第二次做了修改,又把補丁交給了伯克利。 ....然而伯克利的第三次發佈還是老樣子,BBN的程序員徹底失望了。他們沒有再提交補丁,而是把他們所有的Makefile中空格打頭的行替換成了製表符。畢竟BBN僱傭他們是來寫新程序的,而不是反覆修改同一個bug。 (據說,Stu Felman(make的作者)一開始就查覺到了這個問題,他沒有修改,因為那時已經有10個用戶開始用了。) 源碼就是文檔。哇~~ 牛逼!
如果我寫著不容易,那麼你理解起來就不應該容易。 —— 一個Unix程序員我們在《文檔》一章裡提到Unix程序員認為操作系統的源代碼是最好的文檔。一個著名的Unix歷史學家曾經指出:「畢竟,操作系統自己也是靠讀源代碼來知道下一步該幹嘛的。」 可是通過閱讀源代碼來理解Unix,這就如同開著Ken Thompson的老爺車(對,就是閃著大紅問號的那輛)周遊世界。 Unix內核源碼(更準確的說,是ftp.uu.net上發佈的伯克利網絡磁帶2版的代碼)幾乎沒有註釋,充斥這大"段"沒有空行的代碼,goto隨處可見,絞盡腦汁給妄圖讀懂它的人製造麻煩。有個駭客感歎到:「閱讀Unix代碼就好像走在伸手不見五指的巷子裡。我總是停下來摸摸口袋,腦子裡迴響著一個聲音『老天,我就要遭劫了。』」 當然,內核代碼有它自己的警報系統。四處散佈著這樣的小小註釋:
/* XXX */意思是有什麼東西不太對勁兒。你應該知道哪兒出事兒了。 ==這絕不可能是bug,我的Makefile需要它!== BBN的程序員應該算是另類。大部分Unix程序員是不去修改bug的:他們沒有源代碼。即使修改了也於事無補。這就是為什麼Unix程序員遇到bug的第一個反應不是修了它,而是繞過它。 於是我們看到了悲慘的一幕:為什麼不一勞永逸地解決問題,而是一錯再錯?也許早期的Unix程序員是尼采「永恆輪迴」思想的信徒。 對於調試方法,存在著兩個截然不同的派別:一個是「外科手術派」,包括流行於早期ITS和Lisp系統,程序運行過程中始終有調試器參與,如果程序崩潰了,調試器(也就是所謂外科大夫)會對問題進行診斷醫治。 Unix是屬於更古老的「屍體解剖派」。Unix下如果一個程序崩潰了,會遺留下一個core文件,從各個方面看這都和屍體沒什麼兩樣。Unix調試器然後會找出死因。有趣的是,Unix程序常常和人一樣,死於本可治療的疾病、事故以及疏忽。 ==對付Core== 如果你的程序吐核(core)了,你首先要做的是找到它。這不該太困難,因為core文件總是很大——4, 8, 甚至12兆。 core文件之所以這麼大,是因為它包括了所有用來調試的信息:堆棧,數據,代碼指針等等,無所不包,除了程序的動態狀態。如果你在調試一個網絡程序,在你的程序吐核的時候,已經為時太晚了;程序的網絡連接已經沒有了,更致命的一擊是,所有打開的文件現在都被關上了。 不幸的是,在Unix上只能如此。 例如,不能把調試器作為命令解析器,或者在內核發生異常時把控制交給調試器。如果想讓調試器在程序崩潰時進行接管,那你只能在調試器裡面運行所有程序(是的,有的Unix版本讓你用調試器接管一個運行中的進程,但是你手邊必須有一個還有符號的程序文件)。如果你想調試中斷代碼,你的調試器必須截獲每個中斷,然後把合適的中斷返回給程序。你能想像emacs裡每敲一鍵都發生3個進程切換(context switch)的感覺麼? 顯然,例程調試(routine debugging)思想和Unix哲學是格格不入的。 ---- 日期: Wed, 2 Jan 91 07:42:04 PST 發信人: Michael Tiemann
日期: Thu, 17 Jan 91 10:28:11 PST 發信人: Michael TiemannUnix程序員總是打著「這會破壞已有代碼」的幌子,不願意修正bug。可這裡面還有內幕,修正bug不但會破壞已有代碼,還必須修改簡單完美的Unix接口,而這正是Unix教眾們的命根子。至於這個接口是否工作,這並不重要。Unix教眾們不去提出更好的接口,也不去修正bug,而是齊聲高唱「Unix接口好簡潔,好簡潔。Unix接口就是美,就是美!Unix無罪!Unix有理!」。 不幸的是,繞過bug是個很惡劣的行為,它使得錯誤成為了操作系統規範的一部分。你越是等,就越難以修正,因為越來越多的程序會盡力繞過bug,以至於沒有了bug反而活不了了。同理,修改操作系統接口帶來的影響更大,因為更多的程序必須根據這個正確的新接口進行修改。(這解釋了為什麼ls有那麼多的選項來完成幾乎一樣的工作)。 如果你把一隻青蛙仍到開水裡,它會馬上跳出來。它知道開水很燙。可是,如果你把青蛙放到冷水裡,再慢慢地加熱,青蛙感覺不到什麼,直到最後被燙死。 Unix接口已經開鍋了。以前,輸入/輸出的全部接口只包括open, close, read和write。網絡支持給Unix添了一大把柴禾。現在,至少有五種方法向一個文件句柄輸入數據:write, writev, send, sendto和sendmsg。每個都在 內核中有不同的實現,這意味著有五倍的可能出現bug,有五種不同的性能結果需要考慮。讀文件也一樣(read, recv, recvfrom和recvmsg)。等死吧,青蛙們。 ==文件名擴展== Unix「所有程序自成一體」的規定有一個例外。Unix程序經常要處理一個或多個文件。Unix shells提供了命名一組文件的方法,shell會把這組文件展開,做為一個文件列表 傳遞給各個命令。 例如,假設你的目錄下有文件A, B和C。如果象刪除所有這些文件,你可以運行 "rm *"。shell會把"*"擴展成為"A B C",並把他們做為rm的參數傳遞給它。這個方法有不少問題,這在上一章已 經提到過了。不過,你應該知道讓shell來擴展文件名不是偶然的:而是 精心設計的結果。在Kernighan和Mashey發表的《Unix編程環境》一文中(IEEE計算機雜誌,1981年四月),他們指出:「把這個作為shell的一個機制,這避免了各個程序的重複勞動,而且保證了為所有程序提供一致的輸入。」 (Unix的一個理想是 讓任何人能夠運行任何shell。現在你沒法運行任何shell;你的shell必須提供文件名擴展)。 別忙。標準輸入/輸出庫(Unix所謂的stdio)不就能「為所有程序提供一致的輸入」麼?提供一個用於擴展文件名的庫函數不就成了?這些傢伙沒有聽說過鏈接庫麼?那些關於性能的說法也是無稽之談,因為他們無法提供任何的性能數據,他們甚至沒有說明「性能指標」是什麼。指的是開發一個小程序會快一些?還是指能高性能地把一個新手的所有文件一掃而光? 大多數情況下,讓shell進行文件名擴展也無所謂,因為這和程序自己擴展的結果沒什麼不同。可是,和Unix上的許多玩意一樣,它早晚會咬你一口,而且不輕。 假設你是個Unix新手,目錄下有兩個文件A.m和B.m。你習慣了MS-DOS,想把它們的名字換成A.c和B.c。嗯~~ 沒找到rename命令,不過mv命令似乎差不多。於是你執行mv *.m *.c。 shell將這個命令擴展為 mv A.m B.m,你辛辛苦苦寫了幾小時的B.m就這麼被幹掉了。 再好好思考一下上面這個問題,你就會發現理論上你完全不可能提供一個和MS-DOS "rename"一樣的功能。對於軟件工具,就扯這麼多吧。 ==健壯性,或者說「所有輸入行必須小於80個字符」== 1990年11月份的《ACM通訊》上登了Miller Fredriksen等人寫的一篇精采文章,題目是《Unix工具的穩定性的經驗性研究》。他們使用一些隨機數據作為Unix工具的輸入,發現有24-33%(不同的Unix發佈結果有所不同)的工具崩潰了。有時候甚至整個系統都完蛋了。 文章是以一個笑話開頭的。其中一位作者曾使用一個極差的電話連接工作,發現許多工具都垮掉了。於是他決定針對這一現象進行更系統的調查研究。 許多bug都可以歸因於C語言的陳規陋習。事實上,Unix的許多內在腦損傷都是C語言造成的。Unix的核心以及所有的工具程序都是用C語言寫的。著名語言學家Benjamin Whorf說過:語言決定思想。Unix有深深的C烙印。C 語言使得程序員根本無法想像能寫出健壯的程序。 C語言是極小的。它被設計成能在各種硬件上快速地進行編譯,所以它有著和硬件類似的結構。 Unix誕生之初,使用高級語言編寫操作系統是個革命性的想法。現在則應該考慮使用一種有錯誤檢查的語言了。 C是個最為底層的語言,誕生於硬件更為底層的時代。PDP-11沒有的,C語言也不會有。過去幾十年的編程語言研究表明,語言中加入錯誤處理,自動內存管理和抽像數據類型等功能,會使得開發出的程序更為健壯可靠。你在C裡面找不到這些東西。C語言太流行了,沒人去考慮給它增加諸如數據標記或硬件垃圾回收支持等功能。即使硬件提供了垃圾回收功能,也只是多費了一些硅片罷了,因為許多C語言編寫的程序根本無法使用它。 回想一下,C是無法處理整數溢出的。解決方法是使用超過問題需要的整數大小,希望這個大小在你有生之年足夠用。 C也沒有真正意義上的數組,它有個像是數組的東西,實際不過是一個指向一塊內存的指針。數組定位表達式(array[index])不過是表達式(*(array+index))的簡寫版。所以你甚至可以說index[array],這和表達式(*(array+index))是一個意思。聰明吧?在字符處理時經常能見到這個用法。數組變量和指針變量經常可以互換。 舉個例子,假設你有:收信人: UNIX-HATERS 主題: Unix Debuggers (Unix調試器) David Letterman (美國著名晚間脫口秀主持人)的10個最佳理由是: 10. 這會破壞已有代碼。 9. 這需要修改文檔。 8. 太難實現了。 7. 這怎麼是調試器的活兒?為什麼不寫個「工具」做它? 6. 如果調試器吐了核,你應該丟開你自己的程序,開始調試調試器。 5. 太難理解了。 4. 哪兒有餅乾? 3. 為什麼非得現在做? 2. Unix也不是神仙。 1. 哪兒有問題?
char *str = "bugy";於是下面的這些語句都是一樣的:
0[str] == 'b' *(str+1) == 'u' *(2+str) == 'g' str[3] == 'y'==C語言夠偉大的吧?== 這個做法的問題是C根本不做任何自動數組邊界檢查。為什麼該C去做呢?數組在C裡只是個指針而已,你可以把指針指向內存的任何地方,是不是?不過,一般你不想在內存裡亂寫亂畫,特別在是一些關鍵的地方,比如程序的堆棧。 這把我們引到了Miller的文章裡提到的一類bug。許多程序是在讀取輸入到堆棧上的一塊字符緩衝區時崩潰的。許多C程序是這麼做的;下面的C程序把一行輸入讀到堆棧上的一個數組裡,然後調用do_it函數進行處理。
a_function() { char c, buff[80]; int i = 0; while ((c = getchar()) != '\n') buff[i++] = c; buff[i] = '\000'; do_it(buff); }這類代碼把Unix搞得臭不可聞。知道為什麼緩衝區被定為80個字符麼?這是因為許多Unix文件每行最多有80個字符。知道為什麼沒有邊界檢查,也沒有文件尾檢查麼?這是因為這個程序員喜歡把c = getchar()這樣的賦值語句嵌入 到while循環中。信不信,有些人還推崇C的這種縮簡寫法,管他媽什麼可讀性可維護性。最後,調用do_it(),數組搖身一變成了指針,作為第一個參數傳了進去。 ==作為練習:如果在一行當中到達了文件尾,這個程序的結果是什麼?== 當Unix用戶查覺到這個內置的限制後,他們想到的不是去修正這個bug,而是想方設法躲過它。比如,Unix的磁帶備份工具(tape archiver)tar不能處理超過100個字符的路徑名(包括目錄)。解決方 法是:不要備份目錄到磁帶,或者使用dump。更好的辦法是:不要建立太深的目錄,這樣文件的絕對路徑就不會超過100個字符。 2038年1月18日上午10點14分07秒,Unix馬虎編程將在這一刻上演精采的一幕,那時Unix的32位timeval將耗盡... 再回到我們前面那個例子,假設輸入行有85個字符。這個函數毫無問題地接受了這個輸入,可問題是最後那5個字符會被放到哪裡呢?答案是它們會佔據任何排放在數組後面的5個字節。之前那裡放著的是什麼呢? c和i這兩個變量可能會被分配在字符數組之後,所以有可能會被85字符長的輸入衝垮。如果輸入了850個字符呢?則可能會毀掉堆棧上的重要的C運行環境系統信息,比如返回地址等。毀掉這些信息的最好結果是程序可能崩潰。 我們說「可能崩潰」是因為程序的編寫者從沒想到過你竟能毀掉堆棧。想像一下我們的這個程序讀入了很長的一行,約有2,000個字符,這行字符被用來覆蓋堆棧上的返回地址以及其他環境信息,它將調用2,000個字符裡埋藏的一段代碼。這段代碼可能會做一些很有用的事情,比如執行(exec)出一個shell,運行一些命令。 Robert T. Morris的著名Unix蠕蟲病就是使用了這個機制(以及其他一些技巧)黑進Unix主機的。我不知道其他人為什麼還會這麼做,真的不知道,嘻嘻。 ---- 日期: Thu, 2 May 91 18:16:44 PDT 發信人: Jim McDonald
struct bpt *another_function() { struct bpt *result; result = malloc(sizeof(struct bpt)); if (result == 0) { fprintf(stderr, "error: malloc: ???\n"); /* recover gracefully from the error */ [...] return 0; } /* Do something interesting */ [...] return result; }another_function函數分配了一個類型為bpt的結構,返回了一個指向這一結構的指針。這段代碼說明了如何分配內存給這個結構。因為C沒有顯式的異常處理支持,C程序員必須自己去做這件事(就是粗體的那些代碼)。 當然你可以不這麼幹。許多C程序員認為這是小事一樁,從來不做異常處理。他們的程序往往是這樣的:
struct bpt *another_function() { struct bpt *result = malloc(sizeof(struct bpt)); /* Do something interesting */ [...] return result; }多麼簡單,多麼乾淨,大多數系統服務請求都會成功的,是不是?這樣的程序在大多數場合運行良好,直到它們被應用到複雜特殊的地方,往往就會神秘地失效。 Lisp的實現總是包括一個異常處理系統。異常條件包括OUT-OF-MEMORY這樣的名稱,程序員可以為特定的異常提供異常處理函數。這些處理函數在異常發生時被自動調用——程序員不需要介入,也不需要做特殊的檢查。適當地使用,可以讓程序更為健壯。 CLU這樣的編程語言也有內置的異常處理。每個函數定義都有一系列可以發出的異常條件。對異常的顯式支持可以幫助編譯器檢查那些未被處理的異常。CLU程序總是十分健壯,因為編譯器逼著CLU程序員去考慮異常處理問題。C程序是個什麼樣子呢:
日期: 16 dec 88 16:12:13 GMT 主題: Re: GNU Emacs 發信人: debra@alice.UUCP <448@myab.se> lars@myab.se (Lars Pensy)>寫到: ... 所有的程序都應該檢查系統調用(如write)的返回結果,這非常重要。同意,可不幸的是很少有程序在進行讀(read)寫(write)時這麼做。 Unix工具程序一般會檢查open系統調用的返回值,假設所有隨後的read,write和close總會成功。 原因很明顯:程序員很懶,不做錯誤處理程序會顯得更小更快。(這樣你的系統會有更優異的性能表現)。 這封信的作者繼續指出,由於大部分系統工具不對write()等系統調用的返回值進行檢查,系統管理員就必須保證文件系統時時刻刻都有足夠的空間。正是如此:許多Unix程序假設它們可以寫任何成功打開的文件,想寫多少就寫多少。 讀到這裡你可能會皺眉頭,"嗯~~」一下。最為可怕的是,就在《Unix工具的穩定性的經驗性研究》這篇文章的前幾頁,登載了一份報告,說明休斯頓外層空間中心的飛船控制實時數據採集系統是如何轉型為Unix系統的。"嗯~~」 ==捕捉bug是社會所不能接收的== 不去檢查和報告bug,這會使製造商生產的系統顯得似乎更為健壯和強大。更重要的是,如果Unix系統報告每一個錯誤,那麼就根本不會有人去用它!這是活生生的現實。 ---- 日期: Thu, 11 Jan 90 09:07:05 PST 發信人: Daniel Weise