2009年5月11日 星期一

淺談C++例外處理 (前篇)

例外處理是一種程式語言的機制,他是用來偵測和處理一些執行時期的錯誤或異常狀況(Runtime error/exception) ,這也意味著如果程式語言想內建這個機制的話,就必須要有執行環境的支援,直覺上也代表著那段程式碼將會跑得比較慢。然而,在某大部份的狀況,執行時期的異常安全(exception safety)是非常重要的,付出那些代價相對來說是非常值得的。對一般的使用者而言,出現Segmentation fault或跳出一個記憶體無法存取的視窗都不太能被接受了,更不用說那些需要高度安全的程式,就好比汽車的剎車系統或核電廠的控制程式,出現執行時期的異常是需要付出慘痛的代價的。

例外處理機制"內建"在程式語言當中最早是從IBM的PL/I( I是羅馬數字的1 , 1964 Appeared, 1976 ANSI, 1979 ISO) 開始的,後來也有一些物件導向的語言開始跟進,在某些作業系統中也有API來讓某些語言(比如說慣C最喜歡的C)能有例外處理的支援,也有某些語言使用了一些語法甜頭來加強他的功能性(如C#的using block),甚至在新興的腳本語言中都可以看到他們的蹤跡。然而,程式語言內建的例外處理機制其實還蠻多人討厭的,慣C的人也覺得這個機制不太直覺。猶有甚者,認為例外處理機制根本就是無形的goto語法,不僅讓工程師們永遠不知道他們是從哪裡開始變換程式流程的,而且他們覺得使用例外處理機制並不會讓問題變得更簡單,更不用說會破壞程式的結構性了。

程式語言的例外處理機制之所以會引發很多的爭議是在於某些例外處理的情況是可以靠check return code來早期發現的,慣C的人(應該)也已經很習慣使用這種例外處理的機制了(不過有時候會夾帶很多macro),而程式語言所提供的例外處理機制要一般化且具有通透性,在黑箱作業的結果就是很多狀況是會破壞程式的控制流程,畢竟程式如何從例外發生點跑到catch block的細節已由編譯器代為處理了。加上為了能在很多特殊的狀況中都能使用,所以他們的實現會比較困難,而且效能也不是相當好。不過程式語言的例外處理是靠執行環境來支援的,這意味著只有他們可以成為最後一道防線,這是任何編譯時期的機制無法辦到的。

例外處理的語法在程式語言當中算是複雜的,也是比較少被使用的,乃至於少數工程師不是很有辦法完全駕馭,即使是有語法甜頭的支援,有時候也會有一些迷失。那讓我們回到主題來吧,C++的例外處理機制說複雜其實也蠻複雜的,其中一個複雜點在C++可以把物件放在自動變數內。自動變數的一個特性就是可以自動進行建構和解構。由於C++標準明確地定義了程式的control flow從throw(異常發生點) 到 exception handler 被執行前是必須解構區塊內所有已建構成功的自動變數。標準講的很簡單,但這其中卻隱藏了許多的陷阱,我們就來看個例子吧。

class testthrow {
public:
    testthrow() {
       test tx;
        cout<<"testthrow constructor"<<endl;
        throw;
    }
    ~testthrow(){cout<<"testthrow deconstructor"<<endl;}
};

int main() {
    try { testthrow tt; } catch(…) {}
    return 0;
}

這段程式碼大部分的人會覺得tx必須被解構(因為他已經被建構了),很可惜是不會的,即便main中使用try catch的區塊並攔截所有例外。原因出現在 rethrow 的語法當中,C++標準規定了throw保留字如沒有參數,那所表達的語意就是進行 rethrow 。然而,C++標準又規定了在沒有發生 exception 的狀況使用rethrow的話,程式是會被終結的。接下來我們來看第二個例子:

int main() {
    someclass c;
    throw ( std::runtime_error("error!!") );
}

在以上的程式碼中,假設c會建構成功的話,那c會被解構嗎?答案是不一定,基本上這段程式碼在C++的標準當中是沒有定義的。C++標準提到了自動變數要解構時機是在stack unwinding 的時候。然而,C++標準又提到,如果在程式裡沒有定義 exception handler 那無論有沒有做stack unwinding 程式都會被終結,依現行的C++編譯器 VC++ 和g++ 的 libstdc++v3 的實作,c是不會被解構的。

2009年5月1日 星期五

Signed不Signed就是問題所在

每個男人都想慣C,但是要在嵌入式系統這個領域當個慣C,其實也不是那麼容易的,君不見遠方蘋果上的銅板跟嵌入式系統一樣小,失之毫釐可是會差之千里的。Jserv前輩的我是軟體 -- 那些處理器教我的事,點出許多使用C語言進行跨平台開發時,很多狀況並不是cross compile下去就沒事了,號稱可以劈腿無數平台的慣C也是有很多平台的差異需要解決的。然而,姑且不論跨平台開發時,其實想要慣C也需要面對許多陷阱的,最簡單的陷阱像 0.1 * rate 會等於 1/10 * rate嗎? (答案是否定的),modifier的位置的差異性(最著名的是指標和const的位置),更不用說誇平台開發了。我們今天的主題就鎖定在型別轉換系統,看Signed不Signed是怎樣影響每個工程師的。

大家都知道想要慣C是必須要了解C的型態系統(tpye system)的,由於C是有形態系統的語言,所以兩個不同的型態要做運算的時候,有時候並不是說聲我們要相加囉(感謝Jserv前輩提供這個梗)就可以完成的,有必要時是必須作型態的轉換才能進行運算的。這部分的描述主要是在C99規格書(ISO/IEC 9899) 的6.3.1(Arithmetic operands)中。然而,其實規格書跟法律條文一樣都是咬文嚼字的頗複雜難懂的,我們今天就來看看其中的一些規則吧。講到變數的型別轉換,其實大家比較容易遇到的是小的型別要變大的型別,這部分在C語言中是隱性轉換,編譯器會自己把這個細節完成。基本上這個轉換會依造以下的規則來進行:

如果一個整數型態要轉換成另一個整數型態時(_Bool除外),如果新型態能表式原本的值,那值應該維持不變。

這個規則點出一個很重要的事實,就是如果一個整數型態要被放到一個比他大且能表示他的型態中,一般處理器的作法是會對變數進行Signed extend或 Zero extend,其中最常被誤會的轉換莫過於一個變數char c; 使用 (unsigned int) c;來強迫進行型態轉換,那會做Signed extend還是Zero extend?答案是都有可能,如果toolchain的char預設是使用signed type,這個運算式會做 Signed extend,如果char預設是使用unsigned type (如ARM toolchain)那會做 Zero extend。

另外一個重要的算數型別轉換規則在C99規格書(ISO/IEC 9899)的 6.3.1.8 Usual arithmetic conversions中,裡面講的是兩個不同型態的變數要做運算時,那應該轉換到哪個型態來進行運算,裡頭講的也蠻複雜的,用簡單的話來說就是以下的規則:

  1. integer conversion rank:  Rank由高到低依序是 long long > long > int > short > signed char 而 char =  signed char = unsigned char
  2. 兩個運算元都是Signed或Unsigned時,就轉成高Rank的型別
  3. 兩個運算元一個是unsigned (A)一個是signed(B)時
    • Rank(A) >= Rank(B) B轉成A的型態
    • Rank(A) < Rank(B)
      • B型態如果能表示A的值,A轉成B的型態
      • B型態如果不能表示A的值,B轉成A的型態

以上的規則最容易出狀況的點在於64位元處理器LP64模式的unsigned int和long ,由於32位元處理器的long是不能表示unsigned int的值的,所以會根據上述的最後一個規則把long轉型成 unsigned int來做運算,而LP64模式卻會使用倒數第二個規則,把unsigned int轉成long來進行運算,所以當unsigned int i=5566; long = -1時,兩個變數在32位元和64位元LP64中,兩個人的大小關係就會產生了奇妙的變化了。

 

最後我們來談談一個型別系統的除錯工具,由於C語言當中埋藏了許多隱性轉換,這部分是由編譯器來完成的,所以我們可以利用編譯器的除錯資訊來看看編譯器到底幫了我們做些甚麼轉換。GCC可以使用-dr這個參數來dump RTL(GCC的中間表式法),雖然那個檔案對很多人來說是天書,但也並非讓人無法捉摸,以下便是一個簡單的範例。透過RTL我們可以知道,在x86中char是signed type,而c轉成p時會有一個隱性轉換而且是使用sign extend來進行轉換。

C Code:

main()
{
                char c =0xea;
                unsigned int p = c;
}

RTL 片段

;; c = -22
(insn 8 6 0 (set (mem/c/i:QI (plus:SI (reg/f:SI 54 virtual-stack-vars)
                (const_int -1 [0xffffffffffffffff])) [0 c+0 S1 A8])
        (const_int -22 [0xffffffffffffffea])) -1 (nil)
    (nil))

;; p = (unsigned int) c
(insn 10 8 11 (set (reg:SI 60)
        (sign_extend:SI (mem/c/i:QI (plus:SI (reg/f:SI 54 virtual-stack-vars)
                    (const_int -1 [0xffffffffffffffff])) [0 c+0 S1 A8]))) -1 (nil)
    (nil))

2009年4月30日 星期四

啄木鳥理論

以前在求學的時候,對人類學還蠻感興趣的(其實人類學還蠻有趣的,號稱是最具科學性的人文學,以及最具人文性的科學),而人類學之中有一部分就是在探討人類的演化,儘管到現在還充滿了爭議。講到演化,大家的腦海中都能浮現起物競天擇(原文是Natural selection,筆者習慣使用自然選擇這個翻譯)和適者生存(原文是Survival of the fittest,嚴格來說是最適者生存,不是來自物種原始,也不算是個科學描述)這兩個詞。然而,很多人對演化是有很大的誤會,其實鼎鼎大名的物種原始在討論到天擇之前,先討論了人擇這個名詞,當然不是講人定勝天,而是人擇很重要,影響了人類的演化史和今天整個世界,想當然耳,也影響到了資訊世界的演化。

蒼茫大地,誰主沉浮?今天的世界為什麼會是這個樣子呢?其實大家都知道,這個大環境就是裁判。但有些人(希望是少數)總是會拿出種族主義來說明自己的人種天生就很優秀。但了解人類演化史的人會知道,所謂的"文明的基礎,不是人,而是動植物" (Haldane, 1892-1964)。環境對人類的發展速度是有絕對的關係的,而人類的選擇對人類本身和被選擇的事物的未來發展也是有絕對的關係的。現在把焦點移回到我們資訊領域,在這個蒼茫的電腦發展史上,人的選擇對電腦技術的存亡也是有關鍵性的,而被選擇的技術對人類(公司)的發展也是有關鍵性的,一個簡單的選擇,造成的影響可能非常遠大,例子太多了,族繁不及備載。

以史為鏡,可以知興替,用演化的角度來看資訊世界的變化,為的也是了解這個娑婆世界的沉浮。早期的電腦科學家,不論是Alan Turing 還是John von Neumann還是Konrad Zuse…等都是二戰左右的人物,也就是說電腦技術一開始是用來打戰的,所以電腦的發展一直受到軍事發展的影響,其中通訊這個領域影響較深遠,君不見tcp/ip,world wide web,3G其實都是從軍事科技演化來的。茫茫的技術,誰主沉浮?基本上大家普遍認為,技術是會收斂的。在演化生物學中有一個名詞叫趨同演化,可以用來說明這一點。但是,趨同演化這個講法到底正不正確呢?黑格爾不是說過,凡存在的必有理由,那些非主流的呢?

我們就來提提啄木鳥這個神奇的生物吧,啄木的能力對鳥類而言是非常有優勢的神兵利器,能在活木裡找到食物吃,是一件非常重要的事,因為活木比枯木多太多了,活木樹洞的保暖禦敵能力也比枯木好很多,所以啄木鳥在演化上是很成功的,在世界上分佈的範圍相當的廣。然而,要有啄木這個能力,必須要有許多配套條件,這些條件在某些鳥類身上也有一些,但只有啄木鳥擁有所有的配套條件,因此能使用這個神兵利器。電腦技術亦然,好的技術的確是邁向成功的條件,但要享受成功仍然有"許多" 環境因素和"許多"技術結合而成,更需要的是努力不懈的堅持。然而,如果一直害怕失敗就會一事無成,0xlab這一群有理想的年輕人,他們告訴了我們這一點,我們可以拭目以待,看他們怎麼改變台灣軟體業,改變這個世界。

2009年4月26日 星期日

產業革命與編譯技術

農牧業的興起在人類的歷史中是一個非常巨大的里程碑,不少進步史觀的歷史學家都認為,如果沒有農業,我們的生活將會是萬古如長夜,不僅過著茹毛飲血日子,像"阿宅"(或許阿宅這個名詞也不會出現了)如我,每天為了三餐的溫飽而煩憂,根本無法宅在家裡甚麼都不會,更不用說有時間在電腦(??)面前敲敲鍵盤,寫寫心得了。然而,很顯然的,這個進步史觀是有問題的(就顯而易見的就是我們阿宅們用了更不符合投資報酬率的工作時間來換取溫飽了),這算是一個無心插柳柳橙汁的結果,不過這不是我想討論的重點,所以就帶過了。

很多人會問,現今的世界為什麼是現在這個樣子?中古世紀的時候,西歐是個文化科技的輸入國,而現在看似社會保守,不講究科技發展的阿拉伯國家,當時可是科技的輸出國。甚麼事情改變了這個世界?這個問題不算難,很多人都會回答是工業革命,事實上"近因"就是如此,遠因很長,就不多談(對於這個問題,槍砲病菌與鋼鐵是個不錯的專書)。附帶一提的,這個世界的產業革命分成兩大類,一個稱英國式產業革命,一個稱為德國式產業革命,其他國家的產業革命模式基本上是逃不了這兩種模式的範圍的。(宅灣?他是走德國式產業革命的路線)

當一個國家想要發展工業時,其中有一個很重要的指標項目叫基礎建設,如果沒有這些基礎建設,這個社會將無法支撐起一個所謂的"現代化國家"的工業。在這個時期,鋼鐵被稱為工業之母,因為沒有這些鋼鐵,各式各樣的基礎建設就無法完成,一個國家生產鋼鐵的數量,當時就是這個國家工業實力的基礎數據標準(當然在21世紀的今天,鋼鐵仍然還是很重要,但鋼鐵產量一定不是等同於工業實力了)。當然,鋼鐵很重要,但只有鋼鐵是沒啥用的,如果沒有辦法賣出去或製成再製品(這些再製品的價值會比鋼鐵好很多),這些東西其實也無用武之地。

話說回來,現在是21世紀,我們都叫他資訊時代,那資訊時代的基礎建設是甚麼?有些的硬體跟鋼鐵一樣,沒有軟體把他弄成再製品是賣不了多少錢的(可以引伸為,如果沒有軟體跑在intel cpu上,這個硬體其實也賣不了多少錢),理所當然的,"系統軟體"就成為了資訊時代的新基礎建設,這些基礎建設中,其中一個很重要的元素叫"編譯器技術"。沒有編譯器,人們就會被迫使用不親近人類的低階語言,軟體的產能自然會降低,軟體工程的書做過研究,語言不是銀彈,但使用低階語言來開發軟體,開發的效率降低是明顯的。

個人認為台灣編譯器技術的研究和能量算是貧乏的,也間接造成計算機結構的研究受其影響。然而,現在是一個充滿編譯器的世界,身為一個工程師,每次聽到編譯器技術,覺得深不可測(或是腦海忽然浮現起自動機的回憶)而輕言放棄,實著可惜。就個人經驗,編譯器的基礎入門門檻的確稍高,但是其實也不會高攀不起。前輩Jserv寫了一些很好的文章"窮得只剩下 Compiler -- 淺談編譯技術的革命"來說明編譯器技術,並為編譯器技術做了一個深入淺出的剖析,就前輩所言 "編譯技術其實也可以很有趣",在這個充滿編譯器時代,就讓我們 Just Compile It !

2009年4月22日 星期三

海星計畫

大約在公元2000左右,台灣開始推廣「矽導計畫」,並在許多著名的國立大學研究所加收名額,試圖提升研發能量、培育更多產業人才來貫徹這個計畫。除此之外,行政院經濟部還在那些著名國立大學研究所執行了叫一個明星級矽智財(Star IP)的學界科專計畫。這個計畫很多大專院校執行,也不少計畫是跨校合作的,計畫內容涵蓋範圍非常廣,上到應用軟體(如H264),開發工具,作業系統,下到硬體RTL的實現,Cell Library,就連大量生產需要的技術(DFM)也包含在內。以處理器這個項目來說,就涵蓋了MCU,MPU到DSP都有學校在進行研究。

很幸運的,個人因為某種機緣,於2006年左右,受業於某執行矽導計畫的學府中,並在學界科專計畫中從事系統軟體的實現。敝校所執行的計畫是實作一個低功耗的數位訊號處理器核心的計畫(這個計畫是和隔壁學校一起執行的)。雖然資訊工程背景的學生由於必修課的關係,必然對系統軟體有所認識(?),但實作系統軟體又是另外一回事,也因為這個計畫,讓我和系統軟體結下了不解之緣,直到今日,我還是沒辦法擺脫(?)他們,套一句前輩講的話,系統軟體很有趣,資訊工程背景的學生應該去了解他們。

敝校的訊號處理器核心原本沒有名字,大家都叫他Star IP,由於執行Star IP計畫的學校有如繁星(?),所以後來執行計畫的教授就把他取個蠻宅的名字叫作starfish(海星)。海星是個採用TSMC .18製程的低功率數位訊號處理器,速度在180Mhz左右(使用SS的Cell Library),每百萬赫茲功率消耗僅0.11mW(這當然有很多需要消音的地方),multi issue的方式是採用64+32+32的方式進行的,通常是一個ALU運算配上一對Load/Store 眼尖的人馬上就可以看出他和某DSP之間的關係。關於starfish的畢業論文很多,想了解的人不妨去圖書館查一下。

個人對海灣合作委員會(GCC的google 翻譯 = =) 的認知,在參與這個計畫之前,基本上只限於使用,而且並不常使用,那時對自由軟體也沒啥概念,只道是個可以動的東西(?)。對一個處理器來說(當然包含數位訊號處理器),一些系統軟體的基礎建設很重要,沒有這些基礎建設,在好的硬體也無用武之地。在台灣這種充滿中小型企業的環境,由於資源不足(?),用很大,用不用錢(這句話在最近頗流行,可惜自由軟體的free是自由,並不是不用錢)的自由軟體便得到了一個很好的切入點,雖然不夠好,但workable,那就夠了。

在自由軟體的世界中,他們想要寫一個自由的作業系統,但不久後他們便發現沒有自由的基礎建設,這個作業系統就只是泡影,所以一切的基礎建設就得自己來過。 GNU Toolchain 就是扮演了這個角色,它基本上提供了一個還不錯的基礎建設框架,所以我們的系統軟體就在GNU Toolchain上架設起來了。有了這些基礎建設,應用程式工程師就能當起神槍手慣C,(省略了很多很多步驟後)在千鈞一髮的時候,沒有經過長官同意就能讓應用程式在海星上面執行了,王子和公主就從此過著幸福快樂的日子了(?)。

 

後話: 由於海星和某DSP之間有淵源,所以基本上海星的GNU Toolchain是從那邊branch出來的(大約在他們論壇放出原始碼,還在beta的時候)。個人在前半研究生涯是從事Binutils 和 Simulator的開發,後半生涯是從事compiler的研究和開發。另外,值得一提的是newlib,沒有這個新圖書館,王子和公主也不會幸福的 (笑)

2009年4月21日 星期二

現量是殘酷的

資訊科學學海茫茫,領域更是五花八門,典論論文開宗明義就說道了:「文人相輕,自古而然。」這種"相輕"當然不只發生在文人身上,是有很大的機會發生在每個人身上的。由於人是群居的動物,隨著農業的發展,人類的社會是相當複雜的,只要是人跟人之間有所互動,就會有相輕的問題。自古以來,人跟人之間的互相歧視,從來是不缺乏理由的,但在今天的社會中,如果隨意的就扣下自然選擇(比較多人知道的翻譯是天擇)這個大帽子,這會被算成是一種種族歧視,在現在這個社會,絕大部分的狀況還是無法公開的被接受的,至少不敢大聲地談論這個問題。

資訊領域也是一個相當複雜的社會,當然也存在這種問題。尤有甚者,認為文章和藝術之美是非常主觀的,但資訊世界是不一樣的,充滿了現實和數字而殘酷的,因此更加深了這種奇妙的對立關係。XX比較好還是YY比較好的論點和問題就會"油然而生"。然而,資訊領域是一門科學,很多東西是有證據而且是可以被量化的,如果只是淪為意氣之爭,那並沒有太大的意義。讓我們來把自然選擇這個大帽子扣下去,就會知道其實好的技術並不等於能活下來,例子太多了,不繁盛舉,這東西說不定您我桌上都有。(題外話,自然選擇本身是最佳化的問題,也有人模擬自然選擇寫出最佳化的演算法)

文章乃經國之大業,不朽之盛事,技術文章亦然,在網路上寫文章要背負的十字架(最近很流行,好像比背背包還流行XD)其實也蠻重大的,然而小弟略懂人類學,也殷切的期許不要讓自己成為一個種族歧視者。然而在資訊領域,小弟傾向不抱持任何偏見,凡是有需求的領域,如能力所及,應該給予支持和尊重才對。不過"限量是殘酷的",對一個資訊領域的專家來說,最大的問題在於時間是有限的,就如同甘道夫所說的,最算給他們三百輩子,還是會覺得時間不夠用,時間有限,要學的東西無窮,要有所取捨才對,魚與熊掌,如何取捨?大問哉。

新的開始

打開這個新的網誌,其實我心中算是五味雜陳的,究竟有多久沒寫網誌了呢?對我個人來說,算起來也快兩年了。由於為了混一張文憑,忙碌的科專計畫和研究生活讓我無法再繼續更新網誌,由於舊網誌的內容跟我從事的研究相去甚遠,舊的網誌從2007年12月後就再也沒更新了,直到前陣子有長輩問小弟說有多久沒更新網誌了,我才恍然大悟,雖然小弟寫的東西都是一些雜亂無章的殘篇斷簡,偶而還是會有路人停下一瞥的。

在為學時,常常有師長稱讚小弟文筆不錯,但由於我的吋管之跡猶如行雲流水,故不曾被提名參加比賽,是否為孤芳自賞,抑或只是客套話,小弟便不得而知了。然而,最近對我來說,心情是挺複雜的,前輩的熱血給小弟很大的啟發,就像王羲之的蘭亭集序裡提到的:夫人之相與,俯仰一世,或取諸懷抱,晤言一室之內;或因寄所托,放浪形骸之外。雖取舍萬殊,靜躁不同,當其欣於所遇,暫得於己,快然自足,曾不知老之將至矣。

莊子說過:人生猶如白駒過隙,一下而已,以至於美好的時光就只能比過隙更為短暫很多了,就像蘭亭後半所提及的:及其所之既倦,情隨事遷,感慨系之矣。向之所欣,俯仰之間已為陳跡,猶不能不以之興懷;況修短隨化,終期於盡。對於前輩敢夢逐夢的熱忱,小弟給予最大的祝福,對我來說,天下沒有不散的筵席,但是筵席會有再開的時候。雖然前輩總是自謙說踏出一步就算成功了,但我還是由衷的祝福他們鵬程萬里,得到真正的成功。

「想改變未來,最好的時刻就是現在」,希望這個新的網誌能代表著全新的開始,以生理學的角度來看,由於新陳代謝的關係,其實每個人每一天都是一個全新的人。然而,許多人不知道的是,其實人這種生物(只是另一種大型哺乳類?)用了極多的能量幫自己作新陳代謝,這個與生俱來的生物特性給了我們很大的啟發,或許我們生來就是為了日新月異、不斷求新,人生不能停止學習,就在現在,踏出了第一步,也期望能持續到最後一步。