<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1910449797393154462</id><updated>2011-08-26T21:18:57.983+08:00</updated><title type='text'>Just Another Blog: 總統府與台灣人民</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://luse.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>17</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-2771172426980578733</id><published>2011-08-24T13:58:00.003+08:00</published><updated>2011-08-24T14:18:30.489+08:00</updated><title type='text'>COSCUP 2011: Porting Android to Brand-new CPU Architecture 投影片上線</title><content type='html'>&lt;p&gt;很高興小的這次有機會能在 &lt;a href="http://coscup.org/2011/zh-tw/"&gt;COSCUP 2011&lt;/a&gt; 跟大家分享 Android porting 相關的議題。&lt;/p&gt;  &lt;p&gt;雖然對 Google 來說，能跑 Android Device 當然是越多越好，但因為話題性跟廣告性的原因，Android 的 Release cycle 其實相當短。這也造成其實 Android 每個版本只能在某個平台上跑得很好。這對同樣是以ARM為核心的SOC也是個重大的問題。不過我們把焦點放在一些&amp;quot;次等國民&amp;quot;非主流CPU上，那面對的問題就完全不一樣了。&lt;/p&gt;  &lt;p&gt;本次演講使用一個台灣製造的非主流CPU&lt;a href="http://www.andestech.com/"&gt; Andes(台灣心)&lt;/a&gt; 來當成是參考平來，說明怎樣 Porting Android 到一些非主流的CPU上，面對效能的瓶頸，該怎麼最佳化，歡迎大家討論與指教。 &lt;/p&gt;  &lt;div style="width: 425px" id="__ss_8961371"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="Coscup2011: porting android to brand-new cpu architecture " href="http://www.slideshare.net/lusecheng/coscup2011-porting-android-to-brandnew-cpu-architecture" target="_blank"&gt;Coscup2011: porting android to brand-new cpu architecture &lt;/a&gt;&lt;/strong&gt;&lt;iframe height="355" marginheight="0" src="http://www.slideshare.net/slideshow/embed_code/8961371" frameborder="0" width="425" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;    &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/" target="_blank"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/lusecheng" target="_blank"&gt;lusecheng&lt;/a&gt; &lt;/div&gt; &lt;/div&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-2771172426980578733?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/2771172426980578733/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2011/08/coscup-2011-porting-android-to-brand.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/2771172426980578733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/2771172426980578733'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2011/08/coscup-2011-porting-android-to-brand.html' title='COSCUP 2011: Porting Android to Brand-new CPU Architecture 投影片上線'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-1139020954978507023</id><published>2010-08-22T15:40:00.001+08:00</published><updated>2010-08-23T20:29:34.015+08:00</updated><title type='text'>淺談 GCC 中的 trampoline (1)</title><content type='html'>&lt;p&gt;最近正好看到前輩 Jserv 的文章 &lt;a title="GCC 的 nested function 與 trampoline" href="http://blog.linux.org.tw/~jserv/archives/2010/07/gcc_nested_func.html"&gt;GCC 的 nested function 與 trampoline&lt;/a&gt; 以及學長Thinker的 &lt;a href="http://heaven.codemud.com/~thinker/GinGin_CGI.py/show_id_doc/416"&gt;補充說明&lt;/a&gt; 後，由於這個領域也算是小弟的守備範圍，又巧逢本格乾旱已久，也好久沒有文章了，就當成是久旱逢甘霖，搭一下順風車淺談一下這個議題。這篇文章會以 GCC 的觀點來切入 trampoline ，並且提及在不同平台中支援 trampoline 的注意事項。不巧的是GCC 4.5 系列 trampoline 的 API 正好有作一些更改，把一些原本是 Macro 的 API 改成使用 Target Hook 的方式，雖然跟功能性沒有什麼太大的關係，由於小弟還沒跟上 GCC 4.5 系列，為了重現臨場感，所以本篇文章如果有使用到 GCC 的原始碼的話，是使用GCC-4.4 系列的原始碼為範例的，請讀者見諒。&lt;/p&gt;  &lt;p&gt;Trampoline 字面的意思是體操用的彈翻床，所以就有跳(Jump)的意思存在，其實trampoline這個字在計算機科學領域是有多重意義存在的，不過大多跟跳有關就是了，trampoline詳細的定義和意義可以參考英文維基 &lt;a href="http://en.wikipedia.org/wiki/Trampoline_(computers)"&gt;Trampoline (computers)&lt;/a&gt; 條目中的內容，這邊不再贅述。而 GCC中 Trampoline 主要是用來支援一個以前在程式語言界蠻流行的功能 Nested function，而 Nested function 最主要的目的是被設計來實現 &lt;a href="http://en.wikipedia.org/wiki/Information_hiding"&gt;information hiding&lt;/a&gt; 的，&lt;a href="http://en.wikipedia.org/wiki/Information_hiding"&gt;information hiding&lt;/a&gt; 在那個年代被認為是提高軟體工程師生產力的秘密武器，而且也是廣為流行的，就把它想成現在的嵌入式程式阿宅都把灌&amp;quot;吸&amp;quot;當王道這樣就好了。或許讀者就會問說：「好好的 C 語言標準不支援，支援這種奇怪的東西幹麻？」當然除了它可以實現一些很奇特的功能像 &lt;a href="http://www.hokstad.com/how-to-implement-closures.html"&gt;Functional&amp;#160; Language 的closure&lt;/a&gt; 之類的東西，最重要的原因就是歷史因素，這個歷史因素也很簡單，那就是 &lt;a href="http://www.gnu.org/software/libc/"&gt;Glibc&lt;/a&gt; 有用到這個功能，所以 GCC 就支援這個功能了。&lt;/p&gt;  &lt;p&gt;如同上一段所說的，傳統上來說 Nested Function 是被設計來實現 &lt;a href="http://en.wikipedia.org/wiki/Information_hiding"&gt;information hiding&lt;/a&gt; 的，所以變數的Scope (變數的有效範圍) 是 Nested Function 中很重要的議題，就語言學來說，其實變數的Scoping 有分成 Dynamic Scoping 和 Static Scoping，用白話來說，假設每個函式有分成外圈和內圈好了，所謂的 A 比 B 還外圈就是代表 B 被允許存取 A 的資料，而 Static Scoping 的語言代表 A 跟 B 的關係在 Static Time (也就是所謂的 Compiling Time) 決定的，而 Dynamic Scoping 的語言代表 A 跟 B 的關係是在 Dynamic Time (也就是所謂的 Runtime) 決定的。舉個簡單的例子來說明，&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;procudure A      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; procudure B       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; XXXX&lt;/p&gt;    &lt;p&gt;end&lt;/p&gt; procudure C     &lt;br /&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; A()      &lt;br /&gt;end       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;就 Static Scoping 的語言來說 B 是 A 的內圈，可是就 Dynamic Scoping 的語言來說 A 是 C 的內圈。以實作上來說，內圈函式為了存取外圈函式的變數，內圈函式的 Stack Frame 中就必須要建一條 Link 到外圈函式的 Stack Frame 去，而連結到 Static Scope 外圈函式的 Link 稱為 Static Chain 而連結到 Dynamic&amp;#160; Scope 外圈函式的 Link 稱為 Dynamic&amp;#160; Chain 。&lt;/p&gt;  &lt;p&gt;就學理來說，Static Typing 的語言通常會使用 Static Scope ，所以我們偉大的灌&amp;quot;C&amp;quot;哥非常靜態，所以可以想見在C語言上實作 Nested Function 也應該是屬於 Static Scoping 的 ，既然是這樣，所以我們也可以很快的想到 &amp;quot;這樣的系統&amp;quot; 是必須要有 Static Chain 的。所以 GCC 中的 trampoline 需要負責做兩件事情，就是 1.幫將被呼叫的函式產生&amp;#160; Static Chain，然後 2.跳轉到 被呼叫的函式去。GCC 為了減少執行時期的開銷，Static Chain 是存放在一個稱為 Static Chain Register 的暫存器內，想當然爾這個暫存器最好是 Caller-Save Register，而且每個平台會使用不同的Register。以x86 32位元的平台來說，Static Chain Register 是 ECX ，然而 x86-64 ECX已經被當成參數來使用，所以使用R10。最後附帶一提的是，由於 trampoline 的機制必須要在執行在 Stack 上的程式碼，大部分的 RISC Machine ( 因為 instruction 跟 data caches分離) 在trampoline 複製到 Stack 中的時候因為不知道他是指令，所以會產生 Memory 的值跟 Instruction Cache 中的值不一致的狀況，所以在使用這種機制時就必須要 clear instruction cache ，以避免執行到 instruction cache 中的舊指令。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-1139020954978507023?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/1139020954978507023/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2010/08/gcc-trampoline.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/1139020954978507023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/1139020954978507023'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2010/08/gcc-trampoline.html' title='淺談 GCC 中的 trampoline (1)'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-5184756353901701578</id><published>2010-06-07T10:33:00.001+08:00</published><updated>2010-06-07T10:34:20.519+08:00</updated><title type='text'>使用 Chroot 來混用 Ubuntu 10.04 TLS 和 Ubuntu 8.04 LTS</title><content type='html'>&lt;p&gt;&lt;a href="http://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt; 是個非常流行而且算是主流中的主流(?)的一種Linux發行版，也就因為是主流中的主流，所以其實 Ubuntu 中的套件裡面軟體都非常新(不過Ubuntu當一個版本Release後，通常就不會升級裡面軟體的版本主要版本了，通常只進行bugfix 和 patch number的更新)，這對很喜歡用新版的Linux使用者來說，算是一種福音。(當然，如果很喜歡用最新版而且非常想更新主要版本的使用者，通常會選擇Gentoo+使用~符號來嘗鮮)。版本新是很好，可是對軟體開發者來說，卻有個非常重大的缺點，那就是開發出來的二進制的散佈問題。&lt;/p&gt;  &lt;p&gt;或許有的人會問：「那就用 static link 就好了阿，這樣不是一切都解決了？」姑且不提有些函式庫就是沒辦法 static link，還是某些發行版為了 license issue ，所以只提供了share 版本的函式庫 ，其實static link 並不能完全解決二進制散佈問題。聽到筆者這樣說，或許這樣大家就會問了：「static link 不是把所有的東西拉進來了嗎？而且理論上是 Self-contain 的，為什麼還是會遇到問題？」簡單的說，在編譯的時候即使使用了 static link，很清楚的地方是，其實 kernel 並沒有被拉到執行檔內，這樣的話static link的二進制檔嚴格來說是跟 kernel 有些相依性的。&lt;/p&gt;  &lt;p&gt;什麼是跟 kernel 有些相依性，程式開發人員都知道，跟 Linux 打交道的方法就是透過 system call，linux 中的 libc (glibc, eglibc, uclibc and bionic) 就是提供了 C 語言跟 linux kernel 系統呼叫的一個橋樑，為了效能上的考量，新版的 libc 通常會使用某個版本以上的 linux kernel 才提供的 syscall，這樣會造成即使是static link的二進制，也需要在某個版本以上的Linux Kernel才能執行。以Ubuntu來說，8.04 編譯出來的檔案至少要使用2.6.8以上的Linux Kernel才能執行，而10.04卻需要2.6.18以上的Linux Kernel才能執行，這個差距足以讓那些 Red Hat Enterprise Linux AS release 4 或是 Cent OS 4.6 的工作站無法執行在Ubuntu 10.04 開發出來的二進制。&lt;/p&gt;  &lt;p&gt;如果想要升級到 Ubuntu 10.04 LTS 卻害怕相容性問題，想要使用新版本的軟體來開發程式卻想使用 Ubuntu 8.04 LTS 來編譯程式的話，或許有人會想用使用Dual Boot，可是其實不需要那麼麻煩， &lt;strong&gt;&lt;a href="http://en.wikipedia.org/wiki/Chroot"&gt;chroot&lt;/a&gt; &lt;/strong&gt;就是你的救星！ 參考 &lt;a href="http://www.gentoo.org/proj/en/base/amd64/howtos/index.xml?part=1&amp;amp;chap=2"&gt;Gentoo的作法&lt;/a&gt; ，先將Ubuntu 8.04 安裝在某個分割區，然後使用Ubuntu 10.04 開機後，掛載 8.04 的分割區後，執行以下的程式碼 (假設 8.04 的分割區在被掛載在 /mnt )，然後再執行 chroot /mnt /bin/bash ，就能輕鬆享用 8.04 的環境。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;# mount -o bind /dev /mnt/dev      &lt;br /&gt;# mount -o bind /dev/pts /mnt/dev/pts       &lt;br /&gt;# mount -o bind /dev/shm /mnt/dev/shm       &lt;br /&gt;# mount -o bind /proc /mnt/proc       &lt;br /&gt;# mount -o bind /proc/bus/usb /mnt/proc/bus/usb       &lt;br /&gt;# mount -o bind /sys /mnt/sys&lt;/p&gt;&lt;/blockquote&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-5184756353901701578?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/5184756353901701578/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2010/06/ubuntu-1004-tls-ubuntu-804-lts.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/5184756353901701578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/5184756353901701578'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2010/06/ubuntu-1004-tls-ubuntu-804-lts.html' title='使用 Chroot 來混用 Ubuntu 10.04 TLS 和 Ubuntu 8.04 LTS'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-8118081156992764425</id><published>2010-01-09T22:45:00.001+08:00</published><updated>2010-01-09T23:59:05.537+08:00</updated><title type='text'>淺談C++例外處理 (後篇)</title><content type='html'>&lt;p&gt;這一篇是這個系列的完結篇了，重點放在 GCC C++ Compiler 的例外處理機制 - Dwarf2 Exception Handling。看到 &amp;quot;Dwarf&amp;quot; 這個英文單字，如果是執行檔格式有研究的人，大概會想到的東西除了龍與地下城和魔獸爭霸的矮人以外就是debug相關的東西了。沒錯，這邊Dwarf2的意思就跟大家想的那件是一樣，他是一種 &amp;quot;ELF&amp;quot; 格式中的 &lt;a href="http://en.wikipedia.org/wiki/Debugging_data_format"&gt;debugging data format&lt;/a&gt;，全名是 &amp;quot;Debug With Attributed Record Format&amp;quot;。會取這個名字的梗是由於&amp;quot;ELF&amp;quot;是精靈，而&amp;quot;Dwarf&amp;quot;雖然我們叫他為矮人，而他們在北歐神話中其實是一種精靈(黑暗精靈)。&lt;/p&gt;  &lt;p&gt;例外處理的概念並不困難，只是實作卻是比想像中複雜很多，撇開C++標準規定的&amp;quot;拋出異常時必需要解構所有的自動變數&amp;quot;這個複雜的描述不談，其實對程式語言來說，想要正確的回復到那個時候的狀態，只要能取回例外處理區塊 (在 C++中為catch block) 範圍看的到的區域變數值就差不多了。然而，取回區域變數值說起來很容易，事實上卻不容易，以 C++ 這種語言來說，函式的呼叫或是程式區塊是用堆疊的觀念去處理的，更何況編譯器為了最佳化，是有可能把變數放到暫存器內的。所以可想而知編譯器會在函式的開頭和結尾都插入一些代碼來負責這些平衡堆疊這件事。用簡單的話來說，就是編譯器函式的開頭插入把堆疊指標($esp)調整好，並把有用到且該保存的暫存器壓入堆疊內，並且在離開的時候插入了恢復堆疊指標和暫存器的代碼。&lt;/p&gt;  &lt;p&gt;我們看以下的程式碼，如果執行時期 a 呼叫了 b ，且 b 呼叫了 c 的話。&lt;/p&gt;  &lt;p&gt;&amp;#160;&amp;#160; a() call b() call c()&lt;/p&gt;  &lt;p&gt;如果 c 函式是經由例外處理這條路徑回到了 a ( 也就是 c 拋出例外，而且 b 沒有例外處理區域，而 a 是有例外處理區域的話 )，可以而知的是 c&amp;#160; 跟 b 結尾的程式(用編譯器的行話來說叫epilogue) 並沒有機會被呼叫到，也就是說，如果發生這樣的狀況，我們如果想要進行堆疊的回復動作 (Stack Unwinding) 就必須要經過特殊處理了。在&lt;a href="http://luse.blogspot.com/2009/10/c.html"&gt;上一篇&lt;/a&gt;中，我們已經談過了 GCC 的 C++ 編譯器如何使用 C 函式庫中，專門用來處理非本地跳躍的函式 Setjmp/Longjmp 來處理這個問題，而這一篇，我們將會討論GCC 的 C++ 編譯器使用的第二種處理機制，也就是利用 Dawrf2 中的 debug informantion 來處理這個問題。&lt;/p&gt;  &lt;p&gt;那如何利用 debug informantion 來進行 Stack Unwinding 呢？簡單來說，Dawrf2 的除錯資訊當中裡面有一個欄位是專門用來提供frame相關的資訊稱為CFI (Call Frame Infomation)，編譯器當然也準備好每個函式Frame相關的操作如 push register 和 stack / frame pointer 的調整並編碼成debug informantion 存到執行檔當中。 所以當例外發生的時候，C++ Runtime Library 就能解析這個除錯資訊來了解究竟在函式開頭的時候編譯器做了些什麼事，並且把必要回復的資訊恢復回來。由於GCC 的 C++ 編譯器是使用 langauge-specific data area (LSDA) 這個欄位來儲存這部份的資料並且使用了&lt;a href="http://en.wikipedia.org/wiki/LEB128"&gt;LEB128&lt;/a&gt;編碼來進行壓縮。附帶一提的是，由於這種編碼方式的資料存取的時候是 Unaligned，縱使是硬體有支援 Unaligned access 還是會降低存取的效率。而且由於大部分的函式都是aligned，可是這種狀況會讓函式變成Unaligned ，如果dynmaic loader在設計的時候沒有考慮到這一點，在沒有硬體沒有支援Unaligned access 的處理器上，可能就會引發異常了。&amp;#160; &lt;/p&gt;  &lt;p&gt;接下來切入實作部份，&lt;a href="http://luse.blogspot.com/2009/10/c.html"&gt;上一篇&lt;/a&gt;中已經將 GCC C++ Compiler 例外處理的應用程式介面(API) 和 C++程式碼中的 try, throw, catch 區塊和表示式對應的程式碼說明了一次。由於對 GCC C++ Compiler 例外處理的機制來說，使用Dwarf2 Exception Handling 來進行例外處理是不會影響大流程的，只有 Unwinding 的部分是會受到使用Setjmp/Longjmp 或Dwarf2 Exception Handling 影響。基本上來說，兩種例外處理機制並不是相容的，為了區別使用兩種例外處理機制的二進位並且讓他們可以在共用函式庫內共存，兩種機制使用的例外處理API是稍有不同的 ─ 在_Unwind開頭的API中，他們使用了名字相似但不同的函式，這個處理方式也能讓autotool之流的工具可以方便的辨認他們的編譯器是使用哪種例外處理的機制。以下為兩種機制使用的例外處理API的名稱。&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="457"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="212"&gt;         &lt;p&gt;&lt;strong&gt;Dwarf2 (Call Frame)&lt;/strong&gt;&lt;/p&gt;       &lt;/td&gt;        &lt;td valign="top" width="243"&gt;&lt;strong&gt;Setjmp/Longjmp&lt;/strong&gt; &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="212"&gt;         &lt;p&gt;_Unwind_RaiseException&lt;/p&gt;       &lt;/td&gt;        &lt;td valign="top" width="243"&gt;         &lt;p&gt;_Unwind_SjLj_RaiseException&lt;/p&gt;       &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="212"&gt;         &lt;p&gt;_Unwind_ForcedUnwind &lt;/p&gt;       &lt;/td&gt;        &lt;td valign="top" width="243"&gt;         &lt;p&gt;_Unwind_SjLj_ForcedUnwind&lt;/p&gt;       &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="212"&gt;         &lt;p&gt;_Unwind_Resume&amp;#160;&amp;#160; &lt;/p&gt;       &lt;/td&gt;        &lt;td valign="top" width="243"&gt;         &lt;p&gt;_Unwind_SjLj_Resume&lt;/p&gt;       &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="212"&gt;         &lt;p&gt;_Unwind_Resume_or_Rethrow&lt;/p&gt;       &lt;/td&gt;        &lt;td valign="top" width="243"&gt;         &lt;p&gt;_Unwind_SjLj_Resume_or_Rethrow&lt;/p&gt;       &lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;例外處理的三個關鍵字中，以 throw 的處理最複雜，也就是在throw 轉換控制權到catch區塊的時候需要進行Stack Unwind並且進行非本地的跳轉，&lt;a href="http://luse.blogspot.com/2009/10/c.html"&gt;上一篇&lt;/a&gt;中也提到了主要處理throw這個關鍵字語意的例外處理函式就是 _Unwind_RaiseException。其實_Unwind_RaiseException的處理方式也跟SjLj的版本差不多，有比較大差異的地方大概就是尋找handler 的方式 和 跳轉至例外處理函式的方式。基本上尋找handler是利用PC值去Unwind Table查表來尋找例外處理函式，而比較複雜的是跳轉的部分，他用了一個GCC builtin函式 &lt;strong&gt;__builtin_eh_return&lt;/strong&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;do&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160; {&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; long offset = &lt;strong&gt;uw_install_context_1&lt;/strong&gt; ((CURRENT), (TARGET));&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; void *handler = &lt;strong&gt;__builtin_frob_return_addr&lt;/strong&gt; ((TARGET)-&amp;gt;ra);&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;strong&gt;__builtin_eh_return&lt;/strong&gt; (offset, handler);&amp;#160; &lt;br /&gt;&amp;#160; }&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;while (0) &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;以上的程式是_Unwind_RaiseException用來返回例外處理函式的程式碼，&lt;strong&gt;uw_install_context_1&lt;/strong&gt; 是用來恢復handler的暫存器資訊並且計算 Stack Pointer 需要調整的值，第二個函式是用來取得 handler 的位址。第三部份是 &lt;strong&gt;__builtin_eh_return&lt;/strong&gt; ，由於它是一個GCC平台相關的內建函式，所以每個平台的實作稍有不同。大部分平台採取的實作方式是利用一個被大部分的RTOS用來進行 Context Switch 時候技法 ─ 在Stack中更新 pushed return address ，也就是說函式返回的時候就可以自動進行跳轉，然後返回前必須修正Stack Pointer的值，這樣控制權轉移到正確的地方的時候 (某個catch block)，這時堆疊中的值也是會正確的了。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-8118081156992764425?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/8118081156992764425/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2010/01/c.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/8118081156992764425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/8118081156992764425'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2010/01/c.html' title='淺談C++例外處理 (後篇)'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-5057538719846856701</id><published>2009-12-05T14:47:00.001+08:00</published><updated>2009-12-05T14:47:44.562+08:00</updated><title type='text'>Undefine Behavior 和 GCC</title><content type='html'>&lt;p&gt;有個編譯器的測試程式是這樣寫的，基本上這些測試程式會利用執行程式的回傳值來判斷編譯器是否能通過測試。換句話說，只要編譯並執行了這個測試程式而且執行程式的回傳值是0，就代表編譯器通過這個測驗，反之，就代表編譯器無法通過這個測驗。( 註: 呼叫abort將回傳1，也就是代表測試失敗。)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;void f(int i)      &lt;br /&gt;{       &lt;br /&gt;&amp;#160; &lt;strong&gt;i = i &amp;gt; 0 ? i : -i;        &lt;br /&gt;&lt;/strong&gt;&amp;#160; if (i&amp;lt;0)       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; return;       &lt;br /&gt;&amp;#160; abort ();       &lt;br /&gt;}&lt;/p&gt;    &lt;p&gt;int main(int argc, char *argv[])      &lt;br /&gt;{       &lt;br /&gt;&amp;#160; f(INT_MIN);       &lt;br /&gt;&amp;#160; exit (0);       &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;上述程式在進行的其實就是 abs (&lt;em&gt;absolute&lt;/em&gt; &lt;em&gt;value&lt;/em&gt;) 的測試，看起來如此簡單的一個程式，長的雖然如此甜美，可是卻是包藏禍心的。跟電腦上的數值表示法熟的人就會發現其實有某個數字是沒有辦法正確的做&amp;quot;數學上的&amp;quot; &lt;em&gt;absolute&lt;/em&gt; &lt;em&gt;value&lt;/em&gt;，也不難猜的，他就是那個整數的最小值。對整數的最小值來說，他並沒有對應的正數值，所以整數的最小值進行絕對值運算後會溢位。以在ISO C99 spec 當中，對整數的絕對值相關函式的解釋來說，這樣的行為將會是未定義的。既然是未定義的，就會取決於硬體的實作。一般而言，硬體實作絕對值時，通常會有兩種實作方式，第一種就是直接運算，也就是整數的最小時做絕對值後的結果還會是他原本的值(以32位元來說0xf0000000取補數+1還是0xf0000000)，第二種狀況在DSP比較常出現，就是硬體會幫忙做飽和運算，也就是結果會是正的最大值(0xffffffff)。&lt;/p&gt;  &lt;p&gt;編譯器的核心是IR(&lt;a href="http://en.wikipedia.org/wiki/Intermediate_representation"&gt;intermediate representation&lt;/a&gt;)，IR對一個編譯器進行最佳化時的重要程度，是可以用&amp;quot;IR是編譯器的生命&amp;quot;來形容的。 上述程式粗體表示的部分，其實出乎大部分人(?)意料之外的，如果硬體有支援ABS指令的話，大部分的主流編譯器是能產生ABS指令來最佳化這個運算式的。簡單來說，GCC 為了進行最佳化，定義了許多低階的指令範本(Instruction Pattern)，用來match C 語言所代表的運算式，當然中間還兼過了許多跟平台無關的最佳化過程(像Loop Optimization)，如果目標平台的硬體有定義abs的Instruction Pattern，GCC就會拿它來match上面那個運算式，也就是為什麼GCC能選擇性的用上abs指令來最佳化那條運算式。不過就一個未定義的行為而言，有必要去測試它的正確性嗎？&lt;/p&gt;  &lt;p&gt;很可惜的是GCC是支援多個程式語言的前端，這個未定義的表示式在某些語言中是有定義(對，我就是在說你，Java)。JLS (Java Langauge Sepc) 中定義了有號算數運算如果有溢位的話，必須要使用2的補數表示法來 wraps around 。也就是說，在Java中，剛剛那個絕對值運算&amp;quot;必須&amp;quot;也應該要維持原本的值。GCC為了同時支援兩種不相容的Spec，所以就定義了一個編譯器選項-fwrapv來指定編譯器要不要wraps around有號運算的結果。所以上面那支測式程式還是有它存在的意義，其實編譯器在編譯這個程式時，就是要測試-fwrapv這個編譯器選項有沒有實作正確，這對一些只有 &lt;a href="http://en.wikipedia.org/wiki/Saturation_arithmetic"&gt;saturates&lt;/a&gt; 版本ABS指令的CPU/DSP/GPU造成一些困擾，也就是說當-fwrapv這個選項打開的時候，編譯器要清楚的知道不能使用abs的Instruction Pattern去match那個運算式了。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-5057538719846856701?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/5057538719846856701/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/12/undefine-behavior-gcc.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/5057538719846856701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/5057538719846856701'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/12/undefine-behavior-gcc.html' title='Undefine Behavior 和 GCC'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-5325001585283029672</id><published>2009-11-02T15:57:00.001+08:00</published><updated>2009-11-02T15:57:35.698+08:00</updated><title type='text'>Signed不Signed還是問題所在 之 C# Remix 版</title><content type='html'>&lt;p&gt;每個男人都想當慣C，可是在2009年的今天也有不少男人不想當慣C，想當慣西夏(C#)也說不定，跟C和C++比起來，C#是個純物件導向的程式語言，在Everything is object的設計前提上，這些Signed 不Signed 的問題當然使用者不是很想去沾惹，總是希望Framework能把事情處理的很好，這樣王子和公主就能過這幸福快樂的日子了。在這個那麼美好的世界當中，會不會有那些令人惱怒的轉換規則呢？在&lt;a href="http://luse.blogspot.com/2009/05/signedsigned.html"&gt;前篇&lt;/a&gt;中提到的C 語言中Signed Type 跟 Unsigned Type 容易造成的誤會和運算轉換的規則，在現行的純物件導向語言當中，轉換的規則、Signed Type 跟 Unsigned Type 之間的交互作用，又全然是另外一個故事了，本篇以C#為例來探討一下所謂的新世紀Signed 問題。&lt;/p&gt;  &lt;p&gt;當然這整件事情的緣起，是在某朋友的部落閣當中，看到了一個程式。這個程式這這樣寫的。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;unsigned int i=1;     &lt;br /&gt;int j=-1;      &lt;br /&gt;if(i&amp;gt;j) printf(&amp;quot;i&amp;gt;j&amp;quot;);      &lt;br /&gt;else printf(“j&amp;gt;i&amp;quot;);&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;慣C的人看到一定馬上就看出問題所在，大聲說出：「閃開，讓專業的來」，或是「放開那女孩」，然後新警察就會出來說，依造&lt;a href="http://luse.blogspot.com/2009/05/signedsigned.html"&gt;前篇&lt;/a&gt;的規則，這個轉換會根據C99 Spec中的6.3.1.8的Rule 3(a)，得到 &lt;strong&gt;&amp;quot;j&amp;gt;i&amp;quot;&lt;/strong&gt;這個慣C的人看起來很自我感覺良好，可是大家感覺卻不太好的結果。有沒有念過國小的人都知道1是比-1來得大，不過&amp;quot;數學是數學&amp;quot;，&amp;quot;數字要在程式中表現出來&amp;quot;就是會有這樣的結果，數值的表示本來就是有極限，所以才需要訂規則消除這種模糊的狀況，這個應該是程式工程師應該小心的，這是他們的責任。不過把這個看起來詭異的程式拿去C語言的後繼者，也是2000之後才出現的新世紀語言C#去做驗證，卻得到了看起來正常，卻沒有那麼正常的答案&amp;quot;&lt;strong&gt;i&amp;gt;j&amp;quot;&lt;/strong&gt; 。&lt;/p&gt;  &lt;p&gt;看起來正常是法理上是&amp;quot;&lt;strong&gt;i&amp;gt;j&amp;quot;&lt;/strong&gt; 應該，&lt;strong&gt;&amp;quot;j&amp;gt;i&amp;quot;&lt;/strong&gt;會是悲哀，可是如果仔細去想的話，這樣的結果又會令人覺得奇怪，畢竟在C#中什麼都是物件，那個運算子&lt;strong&gt;'=='&lt;/strong&gt;其實會對應到某物件的成員函式，那現在 i 跟 j 的型態並不一樣，那到底是哪個比較函式會被叫起來呢？不過對程式語言稍有理解的人其實會了解，這樣的情境下你的編譯器勢必會幫你動一些手腳，如果你不懂你的編譯器幫你做了些什麼，那可能會是個災難。一個看起來合理的想法可是卻是錯誤的想法就是編譯器會幫你把unsigned int i (C# 中 uint i) 會被轉成 Signed Type，讓這個轉換看起來會得到這樣的結果，可是C#是個號稱自己是個強型態的語言，除了隱性轉換之外(所謂的隱性轉換就是設計者覺得合理的轉換(笑))，使用者是必須在程式碼顯性的說出：「我就是要這個轉換。」然而unsigned int i 轉 int 是會丟失資料的，這樣一個轉換的確不該是一個隱性轉換。會設計成這樣就是不希望使用者管這問題，如果是顯性轉換的話，其實使用者是在意了。&lt;/p&gt;  &lt;p&gt;另外一個合理的實作是把每個基本形態的比較運算子成員函式做不同型態的比較，這樣的話unsigned int 就能正確的跟int 做相比了，不過這樣的排列組合會挺多的，所以並不是一個很好的解法。另外一個合理的轉換方式就是把unsigned int i 跟 int j 都轉成64bit int 再來進行比較，這樣就可以得到合理的結果了，事實上C#的Language Specification中是使用這種解法的，所以C#會得到&amp;quot;&lt;strong&gt;i&amp;gt;j&amp;quot;&lt;/strong&gt;也非常正常。其實就如同其他的語言，不同型態的運算難免不了要做 promotion，只是這樣的作法就見仁見智了。最後，這部份的轉換是定義在C# Spec中的 7.2.6 中，其中的第二節&lt;a name="_Ref452887272"&gt;Binary numeric promotions&lt;/a&gt;就是在這個範例程式碼當中，C#編譯器所套用的轉換規則。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-5325001585283029672?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/5325001585283029672/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/11/signedsigned-c-remix.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/5325001585283029672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/5325001585283029672'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/11/signedsigned-c-remix.html' title='Signed不Signed還是問題所在 之 C# Remix 版'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-6035464757809194008</id><published>2009-10-26T16:25:00.001+08:00</published><updated>2009-10-26T16:48:21.356+08:00</updated><title type='text'>淺談C++例外處理 (中篇)</title><content type='html'>&lt;p&gt;前言: 經過了漫長的混戰當中，終於把GNU G++例外處理的機制實作在某32位元CPU上了，為了避免富姦化，個人考慮多發表一些另類的文章才是阿!!&lt;/p&gt;  &lt;p&gt;前文淺談了C++例外處理的機制，根據&lt;a href="http://luse.blogspot.com/2009/05/c.html"&gt;前篇&lt;/a&gt;的內容，我們可以初略的了解到例外處理機制在語言中的定位和C++例外處理機制中容易產生的迷失。然而，有志者總是喜歡更&amp;quot;深入&amp;quot;的去了解更底層的行為，看似平凡的東西有時候會使人驚艷，而像C++ 例外處理機制這個從Spec 看來就如此複雜機制該如何來實作呢？相信大家心中都有一樣的疑惑。編譯器是個複雜的東西，然而在GNU Toolchain還沒問世之前，要講例外處理的實作通常只能從產生出來的代碼來探討，然而一個開放原始碼的編譯器卻能讓程式工作者能以&amp;quot;編譯器&amp;quot;的角度出發，深入研究C++的例外處理機制，這也是開放原始碼偉大的地方。本文就以開放原始碼的編譯器 GNU G++為出發點，來探討C++例外處理的實作。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;註: 本文探討的原始碼以&lt;a href="http://gcc.gnu.org/"&gt;GCC&lt;/a&gt; 4.4.2 為主&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;GNU Toolchain支援的C++例外處理模型的實作有兩種，與其說例外處理的實作有兩種，其實正確的說法是Stack Unwinder的實作方式有兩種。第一種叫Set Jump Long Jump Exception Handling (簡稱SJLJ) 另一種叫Dawrf 2 Stack Unwinder Exception Handling。如果要用簡單的幾句話來介紹一下兩者皆的差別，其實就是Set Jump Long Jump Exception Handling 這種方式實作起來比較簡單，可攜性也較高，可是效能和產生的代碼大小都差強人意。 而 Dawrf 2 Stack Unwinder是利用Table Driven的方式來進行例外處理，效能和速度都比前者好，想當然是可攜性比較差，而且需要其他二元工具組一些額外的支援，實作上也複雜許多了。本篇的探討會以Set Jump Long Jump Exception Handling為主，而下一篇再探討Dawrf 2 Stack Unwinder。&lt;/p&gt;  &lt;p&gt;Set Jump Long Jump Exception 的原理很簡單，顧名思義就是用到setjmp和longjmp這兩個函式，這兩個函式是C標準函式庫的一員，原本是用來處理non-local goto 的堆疊平衡。所以用他們來處理C++的例外也是蠻單純的。就學理上來說，就是進入到需要被偵測例外的區域就呼叫setjmp來保持執行狀態，在丟出例外時使用longjmp來復原原本的執行狀態。然而事實上這部份的處理確沒那麼簡單，而且比想像中的複雜很多，因為C++程式語言規定了離開例外處理區塊的時候必須要正確的解構auto object，這造成許多麻煩。一個簡單的方法就是把例外處理區塊製造出來需要解構的物件都塞到一個list內，如果例外發生就能依序解構他們。如此一來想當然效能是非常差的，最致命的是，不論例外有沒有發生，這些保存的動作都是必須的。會採取這種實作有許多理由，除了簡單之外，可攜性也是當初在實作的一個重大考量，因為他在那些把C++原始碼轉成C原始碼的早期C++編譯器中也能夠運行的很好，&lt;/p&gt;  &lt;p&gt;回到實作的層面，C++程式語言中用來作例外處理主要的關鍵字有三個，分別是try, catch, 和throw。如果大家用C++程式語言的角度來看，他們的作用我就不贅述，相信C++程式語言的書籍中都有介紹。如果想從標準文件的角度對這三個關鍵字的作用和描述更深入的研究，他們在ISO C++文件的第15章。然而，如果大家用的是更低階的角度來看例外處理，就會發現其實組合語言是沒辦法對這個行為進行原生的支援的。也就是說編譯器其實會根據那些關鍵字，在他們的程式區塊(Code Block)中產生特定的代碼和函式呼叫。關於這個假設，我們可以用GNU G++產生組合語言的功能(-S)來驗證。經過觀察我們就可以輕鬆的發現GNU G++會分別在這三種區塊內加入函式呼叫的代碼。稍微對G++有些了解的讀者會知道其實這些函式名稱其實就是GNU G++的&amp;quot;約定&amp;quot;，也就是在運行時期函式庫中必須要有這些函式，才有辦法正確的進行例外處理。以下為例外處理模型設定成sjlj-exception 的 G++ 編譯器對特定的例外處理關鍵字產生的函式。&lt;/p&gt; &lt;em&gt;try-block&lt;/em&gt;   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; _Unwind_SjLj_Register   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; _Unwind_SjLj_Unregister   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; _Unwind_SjLj_Resume   &lt;br /&gt;&lt;em&gt;handler: (catch block)&lt;/em&gt;   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; __cxa_begin_catch   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; __cxa_end_catch   &lt;br /&gt;&lt;em&gt;throw-expesssion&lt;/em&gt;   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; __cxa_allocate_exception   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; __cxa_throw   &lt;blockquote&gt;   &lt;p&gt;註: 以上的結果必須要將編譯器都設定成 “-&lt;strong&gt;wtih-sjljexception=yes&lt;/strong&gt; “ 才有辦法看到一樣的結果，其中要注意的是大部分主流處理器的編譯器和主流Linux發行版預設的編譯器，預設都是使用Dawrf 2 Exception Handling，所以無法看到相同的結果。&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;_Unwind_SjLj_Register 和_Unwind_SjLj_Unregister分別用來加入或移除一個到Function_Context到unwinding-chain當中。_Unwind_SjLj_Resume是用來恢復例外的傳遞(也就是說用來做cleanup)，前兩個函式的原始碼在&amp;lt;gcc&amp;gt;/gcc/unwind-sjlj.c 中，後面一個在&amp;lt;gcc&amp;gt;/gcc/unwind.inc中。如果大家有去觀看看原始碼會發現其實_Unwind_SjLj_Resume有一個兄弟函式叫_Unwind_SjLj_Resume_or_Rethrow，他是用來處理FORCE_UNWIND例外和實作rethrow的功能__cxa_begin_catch 和__cxa_end_catch就沒那麼複雜了，他們主要用來處理C++例外處理語意中一些housekeeping的動作，他們在libstdc++中libspu++的 eh_catch.cc 內。值得一提的是，C++運行函式庫最容易被問題的問題是有沒有支援thread-safe，然而G++在例外處理的實作是有考慮到thread-safe的。&lt;/p&gt;  &lt;p&gt;throw的處理就複雜很多了，因為throw的處理牽扯到了執行狀態的恢復、例外處理函式的尋找和程式碼控制權的轉移。以下為__cxa_throw的原始碼(在libstdc++/libspu++/eh_throw.cc當中)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;extern &amp;quot;C&amp;quot; void      &lt;br /&gt;__cxxabiv1::__cxa_throw (void *obj, std::type_info *tinfo, void (*dest) (void *))       &lt;br /&gt;{       &lt;br /&gt;&amp;#160; // Definitely a primary.       &lt;br /&gt;&amp;#160; __cxa_refcounted_exception *header       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; = __get_refcounted_exception_header_from_obj (obj);       &lt;br /&gt;&amp;#160; header-&amp;gt;referenceCount = 1;       &lt;br /&gt;&amp;#160; header-&amp;gt;exc.exceptionType = tinfo; &lt;/p&gt;    &lt;p&gt;… 中間省略 …&amp;#160; &lt;/p&gt;    &lt;p&gt;&lt;font color="#ff0000"&gt;&lt;strong&gt;&amp;#160; _Unwind_SjLj_RaiseException (&amp;amp;header-&amp;gt;          &lt;br /&gt;exc.unwindHeader);&lt;/strong&gt;&lt;/font&gt;&amp;#160; &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;&amp;#160; __cxa_begin_catch (&amp;amp;header-&amp;gt;exc.unwindHeader);        &lt;br /&gt;&amp;#160; std::terminate ();         &lt;br /&gt;&lt;/font&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;看過原始碼後我們會發現，其實__cxa_throw並不複雜，比較值得注意的就是藍色的部份，就是如果unwind失敗的時候呼叫std::terminate離開，這也說明了大部分的工作就落是_Unwind_SjLj_RaiseException當中，而且這個函式如果執行成功的話是不會返回到__cxa_throw的。&lt;/p&gt;  &lt;p&gt;_Unwind_SjLj_RaiseException的部份就是分成兩個階段，第一個階段是尋找例外處理函式的部份，這部份的程式會翻閱frame的資訊來尋找是否有例外處理函式被註冊，如果這部份有找到例外處理函式就會進入第二階段，反之就會返回__cxa_throw，這會造成程式被結束。第二階段會進行恢復到有例外處理函式的那個frame的執行狀態，並且取出他的Function_Context並且longjmp回到例外處理函式去。如果某一個函式有多個例外處理函式，那編譯器會在那個函式產生一段充滿跳躍指令的程式碼叫landing pad，而_Unwind_SjLj_RaiseException的會先longjmp回到landing pad後再判斷要跳躍到哪個例外處理函式中。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-6035464757809194008?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/6035464757809194008/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/10/c.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/6035464757809194008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/6035464757809194008'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/10/c.html' title='淺談C++例外處理 (中篇)'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-6580854487434772956</id><published>2009-08-21T11:37:00.001+08:00</published><updated>2009-08-21T15:15:37.627+08:00</updated><title type='text'>我宅 故我在 ~ 餘玹杯心得 [COSCUP 2009]</title><content type='html'>&lt;p&gt;今年是小弟一次參加這個台灣開源/自由軟體界的大活動，也因為今年有不少中意的議程，所以小弟很注意報名資訊，也很幸運的也有報到名，不然4個小時內把450個名額搶購一空的熱浪也太驚人了。由於在活動前一週台灣發生了一件大事，而小弟的老家正好在災區近郊，也蒙受了一點損失，然而比起其他沿海地區算是非常微小了。小的雖然不是政治人物，且能先天下之憂而憂，後天下之樂而樂，曾經那麼熟悉的地方遭受到如此嚴重的天災地變，令人噓唏。也因為心情一直難以平復，就這樣渾渾噩噩過了一個禮拜馬上就到了需要上台北的周末了。&lt;/p&gt;  &lt;p&gt;由於小弟是早上才從新竹趕往台北，加上前晚沒有很早睡(迷之音: 誰叫你要研究HOI3研究到半夜)，又加上路途客運塞車，炎熱的天氣，漫長的椰林道，所以到會場的時候已經午時了，也漏掉了一些議程。不過我第一天最主要的目標是聽人稱人帥真好的Jserv前輩的場次&lt;a href="http://www.youtube.com/group/coscup09#play/user/yEgNnermLf_M//PA6Jmji8Spw"&gt;Linux Virtualization Goes Mobile&lt;/a&gt;，所以我就在外面廠商攤位抽獎聊天&lt;strike&gt;順便看美女&lt;/strike&gt;，正好呼應Jserv前輩那場的主題：「我宅、 我色、我舒服」，不過話說回來，雖然Jserv那場充滿的搞笑的橋段，就Introdutuion 來說是一個的好的演說，不過我猜大部分還是對征服和舒服比較有印象XD。&lt;/p&gt;  &lt;p&gt;第二天的演講對我來說就蠻有吸引力的，早上&lt;a href="http://www.youtube.com/watch?v=xLBEDC9iWV0&amp;amp;feature=PlayList&amp;amp;p=13C2C285B3F464EF&amp;amp;index=67"&gt;Clutter - The Animation UI Framework on Mobile Device&lt;/a&gt;和&lt;a href="http://www.youtube.com/group/coscup09#play/user/Z35bSYPGSt_U/0/29ToLV4rsxo"&gt;開放源碼軟體授權分析工具&lt;/a&gt;就還蠻精采的。&lt;a href="http://www.youtube.com/group/coscup09#play/user/Aza0TLFPYE_8/0/P7OXDnyhCek"&gt;Happy Build with OpenEmbedded&lt;/a&gt;這個主題就比較難發揮了，講者也試圖把他講的精采，開發嵌入式Linux軟體的就會知道OE的好XD。下午的講題就比較硬了，由於個人背景的關係，廖博士那場&amp;quot;Smaller and Faster Android: Optimization and Toolchain Perspective&amp;quot;讓我感覺非常&amp;quot;舒服&amp;quot;，如果Coscup有Best Section Award，我想我會投給他一票。&lt;/p&gt;  &lt;p&gt;總結來說，這個活動辦的很用心，也相當的成功，小小的美中不足點就是以三十分鐘來說，對每個Section似乎是不太夠，其實只要再加個10分鐘，我想大部分的講者可以講得更從容，QA時間的互動也會更加精采，不過我猜這次熱烈的程度，應該可以預期明年會辦的更好，說不定有機會進軍國際會議廳，成為台灣開源/自由軟體界的典範。&lt;/p&gt;  &lt;p&gt;#因為&lt;a href="http://www.blogger.com/profile/03316886314546031469"&gt;大鳥&lt;/a&gt;的建議 改一下標題 for SEO&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-6580854487434772956?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/6580854487434772956/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/08/blog-post.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/6580854487434772956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/6580854487434772956'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/08/blog-post.html' title='我宅 故我在 ~ 餘玹杯心得 [COSCUP 2009]'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-8194756411156441750</id><published>2009-07-26T23:14:00.001+08:00</published><updated>2009-07-26T23:14:41.857+08:00</updated><title type='text'>這就是共和</title><content type='html'>&lt;p&gt;前陣子因為討論雲林國光石化園區的關係，我提出了一個議題「什麼叫共和？」基本上只要是公民就會對政治就會有所關心，所以對於這個已經實行了那麼久的政治制度其實也不陌生。雖然得到的答覆蠻多的，可是錯誤的答覆卻不少，所以就讓我們來討論一下這個影響著現代人類社會非常深遠的政治制度吧。由於小弟是在現代社會出生的，而且我年紀並不大(??)，在懂事的時候台灣社會已經離開獨裁統治(其實獨裁的情狀在&amp;quot;誓死&amp;quot;捍衛共和的傳統共和國也會發生)，並步入了比較正式的民主共和時代，在這邊我並不打算對台灣政治作深入探討，只是想說明在現在出生的人對共和這兩個字並沒有比較特殊的感情，當然也不會有什麼需要誓死捍衛共和的情操。&lt;/p&gt;  &lt;p&gt;台灣的教育對理工科是比較偏重的，自然組學生上高中之後基本上是不看歷史的，所以對最出名的古老共和國&amp;quot;羅馬共和國&amp;quot;了解並不多，僅知道有元老院這東西，而且後來就轉成帝制，史稱大秦，更不用講會去了解裡面的政治制度了。&amp;quot;神鬼戰士&amp;quot;(對岸翻譯成角鬥士)這部電影，讓我對羅馬的帝制和共和產生興趣，&amp;quot;恢復共和&amp;quot;這件事情竟然是如此的重要，讓這個我只理解中國歷史，認為一有機會大家都會想逐鹿中原，問鼎天下的人是完全想不透。後來我因為某些原因開始去研究共和國後期的歷史後，看到了一些人在網路上發表的心得說：&amp;quot;羅馬沒有完全共和過，也沒有完全帝制過，而在凱薩稱帝後再也沒有回到共和&amp;quot;(當然，這三個描述都有錯)，我的心中不禁的重新思考了什麼叫共和？&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;共和制&lt;/strong&gt;的英文是&amp;quot;Repulbic&amp;quot; ，起源於拉丁文的&amp;quot;Res publica&amp;quot;(大概的意思就是公共的事務)。而中文把英文的Repulbic翻譯成共和的原因則是因為在&amp;quot;&lt;a href="http://zh.wikipedia.org/w/index.php?title=%E7%AB%B9%E6%9B%B8%E7%B4%80%E5%B9%B4&amp;amp;variant=zh-tw"&gt;竹書紀年&lt;/a&gt;&amp;quot;當中，周厲王因國人暴動逃離鎬京到到宣王即位這段期間稱為&lt;strong&gt;共和&lt;/strong&gt;(行天子事，號曰『共和元年』，這部份史記周本紀記載的是周公、召公兩相行政)，所以後來日本西化的時候，就引用這個典故，把Republic稱為共和。 就我們所知，農業的起源在西亞，人類因為農業的剩餘糧食，就可以供養一些不需要從事生產的技能專家、統治者和軍隊，統治者接而利用資源進行更大的水力建設來供養更多人口，部落和國家由此產生。上古時期的國家以君主制為主，但已經有一些國家採取共和制(如許多希臘城邦)，可是真正把共和制發揚光大的卻是羅馬共和國。&lt;/p&gt;  &lt;p&gt;羅馬是個非常神奇的城邦共和國，任何一個能維持霸權的國家一定有許多勢均力敵的對手，羅馬就是在這樣的環境下從一個小小的城邦成為一個環繞地中海的大國(他們稱為我們的內海)，在這我就不贅述羅馬的歷史，來探討一下羅馬的政治制度。羅馬人自從革命趕走國王之後，就開始了漫長的共和制度，基本上羅馬走的是寡頭共和這個路線，而且政治上是非常保守的。羅馬最高的行政官叫執政官(Consul)，以現在的國家來說相當於總統，為了避免獨裁者的出現，執政官有&amp;quot;兩名&amp;quot;(其中一個要來自平民)，並且只有一年的任期。不過一向遵循共和傳統的羅馬在敵人兵臨城下時為了避免緊急危難，可由執政官選出一個人來獨裁，稱為獨裁官(Dictator)。會有這樣的職位存在，可以說明其實羅馬人很明確的知道&amp;quot;獨裁&amp;quot;是比較有效率的。(值得一提的是著名的獨裁官凱薩，跟一般人眼中專政獨裁的形象不同，凱薩其實並沒有濫用這個職位，第一次當獨裁官僅十一天而且也沒有福氣利用到終身獨裁官這個職位)&lt;/p&gt;  &lt;p&gt;大家都知道共和國的理想是很遠大的，目的就是要建立一個屬於人民，不屬於個人的國家。但兩千年前的古老共和國告訴我們，縱使今天有了全民參政權，走的是民主共和，但是仔細琢磨後會發現其實今天的政治生態跟兩千多年前其實是相去不遠的，在羅馬共和時期的政客都會喊愛羅馬、愛人民，但大家也都知道他們骨子裡愛的就是錢，愛的是自己。所謂的共和制政府其實只代表著政經高層的意志，並不是大多數平民的意志，任何政客都可以利用施以小惠的方式來換取權利，平民也殊不知這些小惠也是從人民身上搜刮得來的，人民為了過生活，就只好繼續用選票選出吸自己血的政客，其實這就是共和。這一點在媒體如此發達，民智如此開化的今天也無法避免的，也就是說，即使的是2009年的今天，活在共和制底下的人民如果沒有一定的政經關係其實是監督不了政府的。什麼是共和？說穿了就是政經高層的利益。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-8194756411156441750?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/8194756411156441750/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/07/blog-post.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/8194756411156441750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/8194756411156441750'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/07/blog-post.html' title='這就是共和'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-3445533956650281227</id><published>2009-06-07T03:16:00.001+08:00</published><updated>2009-06-07T03:19:26.042+08:00</updated><title type='text'>原諒我就是這樣的字串</title><content type='html'>&lt;p&gt;每個嵌入式系統工程師都想要慣C，可是我們都知道想慣C而不出包也是有難度的，原因是出在於C語言本身存在了許多陷阱，使得就算慣C把所以隱密的資料都砍掉了，貿然去修電腦還是有危險性的。字串，在電腦科學中就是一連串的符號，由於字串跟人類的生活很接近，程式又是用來解決人的問題，所以工程師們在寫程式的時候，也常常面對字串。每種語言對字串的實作方式都不太一樣，即使在同一個語言，每個函式庫也會試圖提供了不一樣實作的字串處理函式庫，這些函式庫大部分都很基本，所以是讓一些初學程式的人練功的好地方。而想慣C的工程師要面對的，就是一個叫c string (或稱為c-style string) 這樣的字串。&lt;/p&gt;  &lt;p&gt;然而，對慣C的人來說 &amp;quot;C-String&amp;quot; 不只是&lt;a href="http://images.google.com/images?hl=zh-TW&amp;amp;rls=com.microsoft:zh-TW:&amp;amp;rlz=1I7GGIH&amp;amp;um=1&amp;amp;sa=1&amp;amp;q=cstring&amp;amp;btnG=搜尋圖片&amp;amp;aq=f&amp;amp;oq="&gt;無帶內褲&lt;/a&gt;，而是一個美麗與哀愁。對C語言來說，字串的表達方式 ─ 也就是所謂的 &lt;a href="http://en.wikipedia.org/wiki/C_string"&gt;c string&lt;/a&gt; (C++族群稱之為 c-style string ) ，其實就只是一個以'\0' (null character) 當結尾 (null terminated) 的字元(或是寬字元)陣列，所以 c string 又被稱為是&lt;b&gt;ASCIIZ&lt;/b&gt; 或 &lt;b&gt;null-terminated string&lt;/b&gt;。然而，由於C語言的陣列(當然不包含C99的變動大小陣列)大小必須是固定的，這個特性加上0結尾就造成了一定的麻煩。對C的初學者來說，少算了0結尾或忽略忘記了0結尾就會變成了一個還蠻常見的錯誤，也誤了不少青春，更不用說固定大小的緩衝區容易造成&lt;a href="http://en.wikipedia.org/wiki/Buffer_overflow"&gt;安全漏洞&lt;/a&gt;。&lt;/p&gt;  &lt;p&gt;為了使用上方便，大部分的語言會提供字串定字(string literals, C99, 6.4.5) ，讓工程師可以在程式碼內表示一些字串。由於C語言是個系統程式語言，所以大家對這些東西會身在何處是有高度興趣的。而 string literals 到底會身在何處，是跟編譯器的實作有關的。C語言規格中定義了他們會被規定分配在&amp;quot;static storage&amp;quot;當中(C99 spec, 6.4.5)，並且說明了如果程式嘗試修改string literals的內容，將會造成未定義行為。以&lt;a href="http://gcc.gnu.org/"&gt;GCC&lt;/a&gt;的 ELF Target 來說，是將string literals分配在read-only data section中(當然包含了0結尾)。由於C語言提供了一些方便的語法甜頭來初始化陣列，這使得 char* p=”hello world”和char p[] = “hello world” 寫法差不多，可是私底下卻是大不同。&lt;/p&gt;  &lt;p&gt;以指標的寫法 char *p 來說，代表的是p將會是指向static storage的一個指標。這樣的寫法是會有一些小問題的，因為嘗試修改string literals的內容，將會造成未定義行為，而這樣的寫法編譯器並不會對存取p的元素提出警告。比較值得注意的是陣列的寫法，依規定 string literals 是必須放在 &amp;quot;static storage&amp;quot;中的，而 char p[] 的語意則表示要把資料分配在Stack內，所以這會造成編譯器隱性的產生執行期把string literals從static storage複製到stack中的代碼，雖然字串本身不是放在Stack內，但char p[]卻是分配在Stack內，這也造成return p是未定義行為。&lt;/p&gt;  &lt;p&gt;光是表示字串有時候是不太夠的，所以必須要在字串上做一些運算，而C語言的規格書中定義了標準函式庫必須提供一些字串處理的函式 ( 這部分定義在C99 spec, 7.21 String handling &amp;lt;string.h&amp;gt;中)，來幫助工程師避免製造重複的輪子。接下來問題來了，這些字串處理函式的原型大部分是使用char *或void * 的型別來當參數，那這些參數到底能不能接受null pointer呢？如果不能，那函式要負責檢查嗎？null pointer算是一個字串嗎 ？對一個null pointer使用這些函式(如strlen)會是怎樣的結果？規格書中有定義這些東西嗎？&lt;/p&gt;  &lt;p&gt;答案是 string handling function *不能接受* null pointer當參數，因為絕大部分的狀況null pointer並不是一個字串(可以思考一下為什麼)，所以對null pointer使用strlen絕大部分是會造成未定義行為的，而大部分的實作也不會在函式庫內做null pointer檢查(有些函式庫的實作會使用編譯器的延伸語法來提出警告)，所以代表工程師是要把null pointer檢查的責任一肩扛下的。大部分的人會把這部分的現象推給編譯器實作的差異，不過到GCC的論壇問這個問題，得到可能會是&amp;quot;由於GCC沒有提供標準函式庫所以這跟GCC無關(笑)&amp;quot;的答案。然而，規格書就像法律條文，有時候是需要解釋的，個人認為其實C99規格書隱晦的提到了這個問題，所以以上的現象都是合理的。原因出在於C99 Spec 7.21.1的第一點中提到了：&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Various methods are used for determining the lengths of the arrays, but in all cases a char * or void * argument points to the initial (lowest addressed) character of the      &lt;br /&gt;array. If an array is accessed beyond the end of an object, the behavior is undefined. &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;null pointer顯然在*絕大部分*的狀況都不符合這個規定，既然不符合規定，函式庫的實作顯然也不需要浪費心力去做檢查。更不用說想要在一些物件導向的字串函式庫中使用 string物件的null pointer 來做字串運算，我想這一定是瘋了(題外話，以大部分物件導向語言使用字串的情形，null時要做的事情其實是跟empty時是差不多的，所以.NET的字串函式庫後來提供了IsNullorEmpty的類別方法，讓工程師不用每次檢查Empty之前還要check 是不是null )。當工程師想慣C而被這些字串的問題所擾，仰天長嘯問 c string 為什麼 要用這個方法來實作時，可能的情形就是 c string 使用了 戴佩妮(&lt;a href="http://www.iampenny.idv.tw/"&gt;Penny&lt;/a&gt;) 新歌的梗來回答說 ：&amp;quot;原諒我就是這樣的字串&amp;quot;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-3445533956650281227?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/3445533956650281227/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/06/blog-post.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/3445533956650281227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/3445533956650281227'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/06/blog-post.html' title='原諒我就是這樣的字串'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-6145623499889291122</id><published>2009-05-11T17:47:00.001+08:00</published><updated>2009-05-11T17:47:13.527+08:00</updated><title type='text'>淺談C++例外處理 (前篇)</title><content type='html'>&lt;p&gt;例外處理是一種程式語言的機制，他是用來偵測和處理一些執行時期的錯誤或異常狀況(Runtime error/exception) ，這也意味著如果程式語言想內建這個機制的話，就必須要有執行環境的支援，直覺上也代表著那段程式碼將會跑得比較慢。然而，在某大部份的狀況，執行時期的異常安全(exception safety)是非常重要的，付出那些代價相對來說是非常值得的。對一般的使用者而言，出現Segmentation fault或跳出一個記憶體無法存取的視窗都不太能被接受了，更不用說那些需要高度安全的程式，就好比汽車的剎車系統或核電廠的控制程式，出現執行時期的異常是需要付出慘痛的代價的。&lt;/p&gt;  &lt;p&gt;例外處理機制&amp;quot;內建&amp;quot;在程式語言當中最早是從IBM的&lt;a href="http://en.wikipedia.org/wiki/PL/I"&gt;PL/I&lt;/a&gt;( I是羅馬數字的1 , 1964 Appeared, 1976 ANSI, 1979 ISO) 開始的，後來也有一些物件導向的語言開始跟進，在某些作業系統中也有API來讓某些語言(比如說慣C最喜歡的C)能有例外處理的支援，也有某些語言使用了一些語法甜頭來加強他的功能性(如C#的using block)，甚至在新興的腳本語言中都可以看到他們的蹤跡。然而，程式語言內建的例外處理機制其實還蠻多人討厭的，慣C的人也覺得這個機制不太直覺。猶有甚者，認為例外處理機制根本就是無形的goto語法，不僅讓工程師們永遠不知道他們是從哪裡開始變換程式流程的，而且他們覺得使用例外處理機制並不會讓問題變得更簡單，更不用說會破壞程式的結構性了。&lt;/p&gt;  &lt;p&gt;程式語言的例外處理機制之所以會引發很多的爭議是在於某些例外處理的情況是可以靠check return code來早期發現的，慣C的人(應該)也已經很習慣使用這種例外處理的機制了(不過有時候會夾帶很多macro)，而程式語言所提供的例外處理機制要一般化且具有通透性，在黑箱作業的結果就是很多狀況是會破壞程式的控制流程，畢竟程式如何從例外發生點跑到catch block的細節已由編譯器代為處理了。加上為了能在很多特殊的狀況中都能使用，所以他們的實現會比較困難，而且效能也不是相當好。不過程式語言的例外處理是靠執行環境來支援的，這意味著只有他們可以成為最後一道防線，這是任何編譯時期的機制無法辦到的。&lt;/p&gt;  &lt;p&gt;例外處理的語法在程式語言當中算是複雜的，也是比較少被使用的，乃至於少數工程師不是很有辦法完全駕馭，即使是有語法甜頭的支援，有時候也會有一些&lt;a href="http://luse.spaces.live.com/blog/cns!4BC6F14334B3CBD!160.entry"&gt;迷失&lt;/a&gt;。那讓我們回到主題來吧，C++的例外處理機制說複雜其實也蠻複雜的，其中一個複雜點在C++可以把物件放在自動變數內。自動變數的一個特性就是可以自動進行建構和解構。由於C++標準明確地定義了程式的control flow從throw(異常發生點) 到 exception handler 被執行前是必須解構區塊內所有已建構成功的自動變數。標準講的很簡單，但這其中卻隱藏了許多的陷阱，我們就來看個例子吧。&lt;/p&gt;  &lt;p&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt; testthrow {    &lt;br /&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;:    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; testthrow() {    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff0000"&gt;&lt;strong&gt; test tx;&lt;/strong&gt;&lt;/font&gt;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; cout&amp;lt;&amp;lt;&amp;quot;testthrow constructor&amp;quot;&amp;lt;&amp;lt;endl;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;throw&lt;/font&gt;;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; ~testthrow(){cout&amp;lt;&amp;lt;&amp;quot;testthrow deconstructor&amp;quot;&amp;lt;&amp;lt;endl;}    &lt;br /&gt;}; &lt;/p&gt;  &lt;p&gt;&lt;font color="#0000ff"&gt;int&lt;/font&gt; main() {    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;try&lt;/font&gt; { testthrow tt; } &lt;font color="#0000ff"&gt;catch&lt;/font&gt;(…) {}    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;return&lt;/font&gt; 0;    &lt;br /&gt;}&lt;/p&gt;  &lt;p&gt;這段程式碼大部分的人會覺得tx必須被解構(因為他已經被建構了)，很可惜是不會的，即便main中使用try catch的區塊並攔截所有例外。原因出現在 rethrow 的語法當中，C++標準規定了&lt;font color="#0000ff"&gt;throw&lt;/font&gt;&lt;font color="#000000"&gt;保留字&lt;/font&gt;如沒有參數，那所表達的語意就是進行 rethrow 。然而，C++標準又規定了在沒有發生 exception 的狀況使用rethrow的話，程式是會被終結的。接下來我們來看第二個例子:&lt;/p&gt;  &lt;p&gt;&lt;font color="#0000ff"&gt;int&lt;/font&gt; main() {    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; someclass c;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;throw &lt;/font&gt;( std::runtime_error(&amp;quot;error!!&amp;quot;) );    &lt;br /&gt;}&lt;/p&gt;  &lt;p&gt;在以上的程式碼中，假設c會建構成功的話，那c會被解構嗎？答案是不一定，基本上這段程式碼在C++的標準當中是沒有定義的。C++標準提到了自動變數要解構時機是在stack unwinding 的時候。然而，C++標準又提到，如果在程式裡沒有定義 exception handler 那無論有沒有做stack unwinding 程式都會被終結，依現行的C++編譯器 VC++ 和g++ 的 libstdc++v3 的實作，c是不會被解構的。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-6145623499889291122?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/6145623499889291122/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/05/c.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/6145623499889291122'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/6145623499889291122'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/05/c.html' title='淺談C++例外處理 (前篇)'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-1832124860117355177</id><published>2009-05-01T23:40:00.001+08:00</published><updated>2009-05-02T00:15:56.382+08:00</updated><title type='text'>Signed不Signed就是問題所在</title><content type='html'>&lt;p&gt;每個男人都想慣C，但是要在嵌入式系統這個領域當個慣C，其實也不是那麼容易的，君不見遠方蘋果上的銅板跟嵌入式系統一樣小，失之毫釐可是會差之千里的。Jserv前輩的&lt;a href="http://blog.linux.org.tw/~jserv/archives/002064.html"&gt;我是軟體 -- 那些處理器教我的事&lt;/a&gt;，點出許多使用C語言進行跨平台開發時，很多狀況並不是cross compile下去就沒事了，號稱可以劈腿無數平台的慣C也是有很多平台的差異需要解決的。然而，姑且不論跨平台開發時，其實想要慣C也需要面對許多陷阱的，最簡單的陷阱像 0.1 * rate 會等於 1/10 * rate嗎? (答案是否定的)，modifier的位置的差異性(最著名的是指標和const的位置)，更不用說誇平台開發了。我們今天的主題就鎖定在型別轉換系統，看Signed不Signed是怎樣影響每個工程師的。 &lt;/p&gt;  &lt;p&gt;大家都知道想要慣C是必須要了解C的型態系統(tpye system)的，由於C是有形態系統的語言，所以兩個不同的型態要做運算的時候，有時候並不是說聲我們要相加囉(感謝Jserv前輩提供這個梗)就可以完成的，有必要時是必須作型態的轉換才能進行運算的。這部分的描述主要是在C99規格書(ISO/IEC 9899) 的6.3.1(Arithmetic operands)中。然而，其實規格書跟法律條文一樣都是咬文嚼字的頗複雜難懂的，我們今天就來看看其中的一些規則吧。講到變數的型別轉換，其實大家比較容易遇到的是小的型別要變大的型別，這部分在C語言中是隱性轉換，編譯器會自己把這個細節完成。基本上這個轉換會依造以下的規則來進行：&lt;/p&gt;  &lt;p align="left"&gt;&lt;strong&gt;如果一個整數型態要轉換成另一個整數型態時(_Bool除外)，如果新型態能表式原本的值，那值應該維持不變。&lt;/strong&gt; &lt;/p&gt;  &lt;p align="left"&gt;這個規則點出一個很重要的事實，就是如果一個整數型態要被放到一個比他大且能表示他的型態中，一般處理器的作法是會對變數進行Signed extend或 Zero extend，其中最常被誤會的轉換莫過於一個變數char c; 使用 (unsigned int) c;來強迫進行型態轉換，那會做Signed extend還是Zero extend？答案是&lt;strong&gt;都有可能&lt;/strong&gt;，如果toolchain的char預設是使用signed type，這個運算式會做 Signed extend，如果char預設是使用unsigned type (如ARM toolchain)那會做 Zero extend。&lt;/p&gt;  &lt;p&gt;另外一個重要的算數型別轉換規則在C99規格書(ISO/IEC 9899)的 6.3.1.8 Usual arithmetic conversions中，裡面講的是兩個不同型態的變數要做運算時，那應該轉換到哪個型態來進行運算，裡頭講的也蠻複雜的，用簡單的話來說就是以下的規則：&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;strong&gt;integer conversion rank&lt;/strong&gt;:&amp;#160; Rank由高到低依序是 long long &amp;gt; long &amp;gt; int &amp;gt; short &amp;gt; signed char 而 char =&amp;#160; signed char = unsigned char &lt;/li&gt;    &lt;li&gt;兩個運算元都是Signed或Unsigned時，就轉成高Rank的型別&lt;/li&gt;    &lt;li&gt;兩個運算元一個是unsigned (A)一個是signed(B)時&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;Rank(A) &amp;gt;= Rank(B) B轉成A的型態&lt;/li&gt;      &lt;li&gt; Rank(A) &amp;lt; Rank(B) &lt;/li&gt;      &lt;ul&gt;       &lt;li&gt;B型態如果能表示A的值，A轉成B的型態&lt;/li&gt;        &lt;li&gt;B型態如果不能表示A的值，B轉成A的型態&lt;/li&gt;     &lt;/ul&gt;   &lt;/ul&gt; &lt;/ol&gt;  &lt;p&gt;以上的規則最容易出狀況的點在於&lt;a href="http://en.wikipedia.org/wiki/64-bit"&gt;64位元處理器&lt;/a&gt;LP64模式的unsigned int和long ，由於32位元處理器的long是不能表示unsigned int的值的，所以會根據上述的最後一個規則把long轉型成 unsigned int來做運算，而LP64模式卻會使用倒數第二個規則，把unsigned int轉成long來進行運算，所以當unsigned int i=5566; long = -1時，兩個變數在32位元和64位元LP64中，兩個人的大小關係就會產生了奇妙的變化了。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;最後我們來談談一個型別系統的除錯工具，由於C語言當中埋藏了許多隱性轉換，這部分是由編譯器來完成的，所以我們可以利用編譯器的除錯資訊來看看編譯器到底幫了我們做些甚麼轉換。GCC可以使用-dr這個參數來dump &lt;a href="http://en.wikipedia.org/wiki/Register_Transfer_Language"&gt;RTL&lt;/a&gt;(GCC的&lt;a href="http://en.wikipedia.org/wiki/Intermediate_language"&gt;中間表式法&lt;/a&gt;)，雖然那個檔案對很多人來說是天書，但也並非讓人無法捉摸，以下便是一個簡單的範例。透過RTL我們可以知道，在x86中char是signed type，而c轉成p時會有一個隱性轉換而且是使用sign extend來進行轉換。&lt;/p&gt;  &lt;p&gt;C Code: &lt;/p&gt;  &lt;p&gt;main()   &lt;br /&gt;{    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; char c =0xea;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; unsigned int p = c;    &lt;br /&gt; }    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;RTL 片段&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;;; c = -22&lt;/strong&gt;    &lt;br /&gt;(insn 8 6 0 (set (mem/c/i:QI (plus:SI (reg/f:SI 54 virtual-stack-vars)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (const_int -1 [0xffffffffffffffff])) [0 c+0 S1 A8])    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (const_int -22 [0xffffffffffffffea])) -1 (nil)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; (nil)) &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;;; p = (unsigned int) c&lt;/strong&gt;    &lt;br /&gt;(insn 10 8 11 (set (reg:SI 60)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (&lt;strong&gt;sign_extend&lt;/strong&gt;:SI (mem/c/i:QI (plus:SI (reg/f:SI 54 virtual-stack-vars)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (const_int -1 [0xffffffffffffffff])) [0 c+0 S1 A8]))) -1 (nil)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; (nil)) &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-1832124860117355177?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/1832124860117355177/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/05/signedsigned.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/1832124860117355177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/1832124860117355177'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/05/signedsigned.html' title='Signed不Signed就是問題所在'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-5703224242082029647</id><published>2009-04-30T16:45:00.001+08:00</published><updated>2009-04-30T16:49:37.742+08:00</updated><title type='text'>啄木鳥理論</title><content type='html'>&lt;p&gt;以前在求學的時候，對&lt;a href="http://zh.wikipedia.org/w/index.php?title=%E4%BA%BA%E9%A1%9E%E5%AD%B8&amp;amp;variant=zh-tw"&gt;人類學&lt;/a&gt;還蠻感興趣的(其實人類學還蠻有趣的，號稱是最具科學性的人文學，以及最具人文性的科學)，而人類學之中有一部分就是在探討人類的演化，儘管到現在還充滿了爭議。講到演化，大家的腦海中都能浮現起物競天擇(原文是Natural selection，筆者習慣使用自然選擇這個翻譯)和適者生存(原文是Survival of the fittest，嚴格來說是最適者生存，不是來自物種原始，也不算是個科學描述)這兩個詞。然而，很多人對演化是有很大的誤會，其實鼎鼎大名的&lt;a href="http://en.wikipedia.org/wiki/On_the_Origin_of_Species"&gt;物種原始&lt;/a&gt;在討論到天擇之前，先討論了人擇這個名詞，當然不是講人定勝天，而是人擇很重要，影響了人類的演化史和今天整個世界，想當然耳，也影響到了資訊世界的演化。&lt;/p&gt;  &lt;p&gt;蒼茫大地，誰主沉浮？今天的世界為什麼會是這個樣子呢？其實大家都知道，這個大環境就是裁判。但有些人(希望是少數)總是會拿出種族主義來說明自己的人種天生就很優秀。但了解人類演化史的人會知道，所謂的&amp;quot;文明的基礎，不是人，而是動植物&amp;quot; (&lt;a href="http://en.wikipedia.org/wiki/J._B._S._Haldane"&gt;Haldane&lt;/a&gt;, 1892-1964)。環境對人類的發展速度是有絕對的關係的，而人類的選擇對人類本身和被選擇的事物的未來發展也是有絕對的關係的。現在把焦點移回到我們資訊領域，在這個蒼茫的電腦發展史上，人的選擇對電腦技術的存亡也是有關鍵性的，而被選擇的技術對人類(公司)的發展也是有關鍵性的，一個簡單的選擇，造成的影響可能非常遠大，例子太多了，族繁不及備載。&lt;/p&gt;  &lt;p&gt;以史為鏡，可以知興替，用演化的角度來看資訊世界的變化，為的也是了解這個娑婆世界的沉浮。早期的電腦科學家，不論是&lt;a title="http://en.wikipedia.org/wiki/Alan_Turing" href="http://en.wikipedia.org/wiki/Alan_Turing"&gt;Alan Turing&lt;/a&gt; 還是&lt;a href="http://en.wikipedia.org/wiki/John_von_Neumann"&gt;John von Neumann&lt;/a&gt;還是&lt;a href="http://en.wikipedia.org/wiki/Konrad_Zuse"&gt;Konrad Zuse&lt;/a&gt;…等都是二戰左右的人物，也就是說電腦技術一開始是用來打戰的，所以電腦的發展一直受到軍事發展的影響，其中通訊這個領域影響較深遠，君不見tcp/ip,world wide web,3G其實都是從軍事科技演化來的。茫茫的技術，誰主沉浮？基本上大家普遍認為，技術是會收斂的。在演化生物學中有一個名詞叫&lt;a href="http://en.wikipedia.org/wiki/Convergent_evolution"&gt;趨同演化&lt;/a&gt;，可以用來說明這一點。但是，&lt;a href="http://en.wikipedia.org/wiki/Convergent_evolution"&gt;趨同演化&lt;/a&gt;這個講法到底正不正確呢？黑格爾不是說過，凡存在的必有理由，那些非主流的呢？&lt;/p&gt;  &lt;p&gt;我們就來提提啄木鳥這個神奇的生物吧，啄木的能力對鳥類而言是非常有優勢的神兵利器，能在活木裡找到食物吃，是一件非常重要的事，因為活木比枯木多太多了，活木樹洞的保暖禦敵能力也比枯木好很多，所以啄木鳥在演化上是很成功的，在世界上分佈的範圍相當的廣。然而，要有啄木這個能力，必須要有許多配套條件，這些條件在某些鳥類身上也有一些，但只有啄木鳥擁有所有的配套條件，因此能使用這個神兵利器。電腦技術亦然，好的技術的確是邁向成功的條件，但要享受成功仍然有&amp;quot;許多&amp;quot; 環境因素和&amp;quot;許多&amp;quot;技術結合而成，更需要的是努力不懈的堅持。然而，如果一直害怕失敗就會一事無成，&lt;a href="http://www.0xlab.com/index.html"&gt;0xlab&lt;/a&gt;這一群有理想的年輕人，他們告訴了我們這一點，我們可以拭目以待，看他們怎麼改變台灣軟體業，改變這個世界。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-5703224242082029647?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/5703224242082029647/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post_30.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/5703224242082029647'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/5703224242082029647'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post_30.html' title='啄木鳥理論'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-7984372097594985704</id><published>2009-04-26T01:09:00.001+08:00</published><updated>2009-04-26T01:09:03.803+08:00</updated><title type='text'>產業革命與編譯技術</title><content type='html'>&lt;p&gt;農牧業的興起在人類的歷史中是一個非常巨大的里程碑，不少進步史觀的歷史學家都認為，如果沒有農業，我們的生活將會是萬古如長夜，不僅過著茹毛飲血日子，像&amp;quot;阿宅&amp;quot;(或許阿宅這個名詞也不會出現了)如我，每天為了三餐的溫飽而煩憂，根本無法宅在家裡甚麼都不會，更不用說有時間在電腦(??)面前敲敲鍵盤，寫寫心得了。然而，很顯然的，這個進步史觀是有問題的(就顯而易見的就是我們阿宅們用了更不符合投資報酬率的工作時間來換取溫飽了)，這算是一個無心插柳柳橙汁的結果，不過這不是我想討論的重點，所以就帶過了。&lt;/p&gt;  &lt;p&gt;很多人會問，現今的世界為什麼是現在這個樣子？中古世紀的時候，西歐是個文化科技的輸入國，而現在看似社會保守，不講究科技發展的阿拉伯國家，當時可是科技的輸出國。甚麼事情改變了這個世界？這個問題不算難，很多人都會回答是工業革命，事實上&amp;quot;近因&amp;quot;就是如此，遠因很長，就不多談(對於這個問題，&lt;a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010069715"&gt;槍砲病菌與鋼鐵&lt;/a&gt;是個不錯的專書)。附帶一提的，這個世界的產業革命分成兩大類，一個稱英國式產業革命，一個稱為德國式產業革命，其他國家的產業革命模式基本上是逃不了這兩種模式的範圍的。(宅灣?他是走德國式產業革命的路線)&lt;/p&gt;  &lt;p&gt;當一個國家想要發展工業時，其中有一個很重要的指標項目叫基礎建設，如果沒有這些基礎建設，這個社會將無法支撐起一個所謂的&amp;quot;現代化國家&amp;quot;的工業。在這個時期，鋼鐵被稱為工業之母，因為沒有這些鋼鐵，各式各樣的基礎建設就無法完成，一個國家生產鋼鐵的數量，當時就是這個國家工業實力的基礎數據標準(當然在21世紀的今天，鋼鐵仍然還是很重要，但鋼鐵產量一定不是等同於工業實力了)。當然，鋼鐵很重要，但只有鋼鐵是沒啥用的，如果沒有辦法賣出去或製成再製品(這些再製品的價值會比鋼鐵好很多)，這些東西其實也無用武之地。&lt;/p&gt;  &lt;p&gt;話說回來，現在是21世紀，我們都叫他資訊時代，那資訊時代的基礎建設是甚麼？有些的硬體跟鋼鐵一樣，沒有軟體把他弄成再製品是賣不了多少錢的(可以引伸為，如果沒有軟體跑在intel cpu上，這個硬體其實也賣不了多少錢)，理所當然的，&amp;quot;系統軟體&amp;quot;就成為了資訊時代的新基礎建設，這些基礎建設中，其中一個很重要的元素叫&amp;quot;編譯器技術&amp;quot;。沒有編譯器，人們就會被迫使用不親近人類的低階語言，軟體的產能自然會降低，軟體工程的書做過研究，語言不是銀彈，但使用低階語言來開發軟體，開發的效率降低是明顯的。 &lt;/p&gt;  &lt;p&gt;個人認為台灣編譯器技術的研究和能量算是貧乏的，也間接造成計算機結構的研究受其影響。然而，現在是一個充滿編譯器的世界，身為一個工程師，每次聽到編譯器技術，覺得深不可測(或是腦海忽然浮現起自動機的回憶)而輕言放棄，實著可惜。就個人經驗，編譯器的基礎入門門檻的確稍高，但是其實也不會高攀不起。前輩&lt;a href="http://blog.linux.org.tw/~jserv/"&gt;Jserv&lt;/a&gt;寫了一些很好的文章&amp;quot;&lt;a href="http://blog.linux.org.tw/~jserv/archives/002101.html"&gt;窮得只剩下 Compiler -- 淺談編譯技術的革命&lt;/a&gt;&amp;quot;來說明編譯器技術，並為編譯器技術做了一個深入淺出的剖析，就前輩所言 &amp;quot;編譯技術其實也可以很有趣&amp;quot;，在這個充滿編譯器時代，就讓我們 Just Compile It !&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-7984372097594985704?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/7984372097594985704/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post_26.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/7984372097594985704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/7984372097594985704'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post_26.html' title='產業革命與編譯技術'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-2349181164092089802</id><published>2009-04-22T15:32:00.001+08:00</published><updated>2009-04-22T15:34:15.930+08:00</updated><title type='text'>海星計畫</title><content type='html'>&lt;p&gt;大約在公元2000左右，台灣開始推廣「矽導計畫」，並在許多著名的國立大學研究所加收名額，試圖提升研發能量、培育更多產業人才來貫徹這個計畫。除此之外，行政院經濟部還在那些著名國立大學研究所執行了叫一個明星級矽智財(Star IP)的學界科專計畫。這個計畫很多大專院校執行，也不少計畫是跨校合作的，計畫內容涵蓋範圍非常廣，上到應用軟體(如H264)，開發工具，作業系統，下到硬體RTL的實現，Cell Library，就連大量生產需要的技術(&lt;a href="http://en.wikipedia.org/wiki/Design_for_manufacturability_(IC)"&gt;DFM&lt;/a&gt;)也包含在內。以處理器這個項目來說，就涵蓋了MCU，MPU到DSP都有學校在進行研究。&lt;/p&gt;  &lt;p&gt;很幸運的，個人因為某種機緣，於2006年左右，受業於某執行矽導計畫的學府中，並在學界科專計畫中從事系統軟體的實現。敝校所執行的計畫是實作一個低功耗的數位訊號處理器核心的計畫(這個計畫是和隔壁學校一起執行的)。雖然資訊工程背景的學生由於必修課的關係，必然對系統軟體有所認識(?)，但實作系統軟體又是另外一回事，也因為這個計畫，讓我和系統軟體結下了不解之緣，直到今日，我還是沒辦法擺脫(?)他們，套一句前輩講的話，系統軟體很有趣，資訊工程背景的學生應該去了解他們。&lt;/p&gt;  &lt;p&gt;敝校的訊號處理器核心原本沒有名字，大家都叫他Star IP，由於執行Star IP計畫的學校有如繁星(?)，所以後來執行計畫的教授就把他取個蠻宅的名字叫作starfish(海星)。海星是個採用TSMC .18製程的低功率數位訊號處理器，速度在180Mhz左右(使用SS的Cell Library)，每百萬赫茲功率消耗僅0.11mW(這當然有很多需要消音的地方)，multi issue的方式是採用64+32+32的方式進行的，通常是一個ALU運算配上一對Load/Store &lt;strike&gt;眼尖的人馬上就可以看出他和某DSP之間的關係&lt;/strike&gt;。關於starfish的畢業論文很多，想了解的人不妨去圖書館查一下。&lt;/p&gt;  &lt;p&gt;個人對海灣合作委員會(GCC的google 翻譯 = =) 的認知，在參與這個計畫之前，基本上只限於使用，而且並不常使用，那時對自由軟體也沒啥概念，只道是個可以動的東西(?)。對一個處理器來說(當然包含數位訊號處理器)，一些系統軟體的基礎建設很重要，沒有這些基礎建設，在好的硬體也無用武之地。在台灣這種充滿中小型企業的環境，由於資源不足(?)，用很大，用不用錢(這句話在最近頗流行，可惜自由軟體的free是自由，並不是不用錢)的自由軟體便得到了一個很好的切入點，雖然不夠好，但workable，那就夠了。&lt;/p&gt;  &lt;p&gt;在自由軟體的世界中，他們想要寫一個自由的作業系統，但不久後他們便發現沒有自由的基礎建設，這個作業系統就只是泡影，所以一切的基礎建設就得自己來過。 GNU Toolchain 就是扮演了這個角色，它基本上提供了一個還不錯的基礎建設框架，所以我們的系統軟體就在GNU Toolchain上架設起來了。有了這些基礎建設，應用程式工程師就能當起神槍手慣C，(省略了很多很多步驟後)在千鈞一髮的時候，沒有經過長官同意就能讓應用程式在海星上面執行了，王子和公主就從此過著幸福快樂的日子了(?)。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;後話: 由於海星&lt;strike&gt;和某DSP之間有淵源&lt;/strike&gt;，所以基本上海星的GNU Toolchain是從那邊branch出來的(大約在他們論壇放出原始碼，還在beta的時候)。個人在前半研究生涯是從事Binutils 和 Simulator的開發，後半生涯是從事compiler的研究和開發。另外，值得一提的是newlib，沒有這個新圖書館，王子和公主也不會幸福的 (笑)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-2349181164092089802?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/2349181164092089802/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post_22.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/2349181164092089802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/2349181164092089802'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post_22.html' title='海星計畫'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-3307837080847906817</id><published>2009-04-21T16:06:00.001+08:00</published><updated>2009-04-21T16:06:39.842+08:00</updated><title type='text'>現量是殘酷的</title><content type='html'>&lt;p&gt;資訊科學學海茫茫，領域更是五花八門，典論論文開宗明義就說道了：「文人相輕，自古而然。」這種&amp;quot;相輕&amp;quot;當然不只發生在文人身上，是有很大的機會發生在每個人身上的。由於人是群居的動物，隨著農業的發展，人類的社會是相當複雜的，只要是人跟人之間有所互動，就會有相輕的問題。自古以來，人跟人之間的互相歧視，從來是不缺乏理由的，但在今天的社會中，如果隨意的就扣下自然選擇(比較多人知道的翻譯是天擇)這個大帽子，這會被算成是一種種族歧視，在現在這個社會，絕大部分的狀況還是無法公開的被接受的，至少不敢大聲地談論這個問題。&lt;/p&gt;  &lt;p&gt;資訊領域也是一個相當複雜的社會，當然也存在這種問題。尤有甚者，認為文章和藝術之美是非常主觀的，但資訊世界是不一樣的，充滿了現實和數字而殘酷的，因此更加深了這種奇妙的對立關係。XX比較好還是YY比較好的論點和問題就會&amp;quot;油然而生&amp;quot;。然而，資訊領域是一門科學，很多東西是有證據而且是可以被量化的，如果只是淪為意氣之爭，那並沒有太大的意義。讓我們來把自然選擇這個大帽子扣下去，就會知道其實好的技術並不等於能活下來，例子太多了，不繁盛舉，這東西說不定您我桌上都有。(題外話，自然選擇本身是最佳化的問題，也有人模擬自然選擇寫出最佳化的演算法)&lt;/p&gt;  &lt;p&gt;文章乃經國之大業，不朽之盛事，技術文章亦然，在網路上寫文章要背負的十字架(最近很流行，好像比背背包還流行XD)其實也蠻重大的，然而小弟略懂人類學，也殷切的期許不要讓自己成為一個種族歧視者。然而在資訊領域，小弟傾向不抱持任何偏見，凡是有需求的領域，如能力所及，應該給予支持和尊重才對。不過&amp;quot;限量是殘酷的&amp;quot;，對一個資訊領域的專家來說，最大的問題在於時間是有限的，就如同甘道夫所說的，最算給他們三百輩子，還是會覺得時間不夠用，時間有限，要學的東西無窮，要有所取捨才對，魚與熊掌，如何取捨？大問哉。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-3307837080847906817?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/3307837080847906817/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post_21.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/3307837080847906817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/3307837080847906817'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post_21.html' title='現量是殘酷的'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1910449797393154462.post-822629577857921238</id><published>2009-04-21T01:19:00.001+08:00</published><updated>2009-04-21T01:19:53.188+08:00</updated><title type='text'>新的開始</title><content type='html'>&lt;p&gt;打開這個新的網誌，其實我心中算是五味雜陳的，究竟有多久沒寫網誌了呢？對我個人來說，算起來也快兩年了。由於為了混一張文憑，忙碌的科專計畫和研究生活讓我無法再繼續更新網誌，由於舊網誌的內容跟我從事的研究相去甚遠，&lt;a href="http://luse.spaces.live.com/"&gt;舊的網誌&lt;/a&gt;從2007年12月後就再也沒更新了，直到前陣子有長輩問小弟說有多久沒更新網誌了，我才恍然大悟，雖然小弟寫的東西都是一些雜亂無章的殘篇斷簡，偶而還是會有路人停下一瞥的。&lt;/p&gt;  &lt;p&gt;在為學時，常常有師長稱讚小弟文筆不錯，但由於我的吋管之跡猶如行雲流水，故不曾被提名參加比賽，是否為孤芳自賞，抑或只是客套話，小弟便不得而知了。然而，最近對我來說，心情是挺複雜的，前輩的熱血給小弟很大的啟發，就像王羲之的蘭亭集序裡提到的：夫人之相與，俯仰一世，或取諸懷抱，晤言一室之內；或因寄所托，放浪形骸之外。雖取舍萬殊，靜躁不同，當其欣於所遇，暫得於己，快然自足，曾不知老之將至矣。&lt;/p&gt;  &lt;p&gt;莊子說過：人生猶如白駒過隙，一下而已，以至於美好的時光就只能比過隙更為短暫很多了，就像蘭亭後半所提及的：及其所之既倦，情隨事遷，感慨系之矣。向之所欣，俯仰之間已為陳跡，猶不能不以之興懷；況修短隨化，終期於盡。對於前輩敢夢逐夢的熱忱，小弟給予最大的祝福，對我來說，天下沒有不散的筵席，但是筵席會有再開的時候。雖然前輩總是自謙說踏出一步就算成功了，但我還是由衷的祝福他們鵬程萬里，得到真正的成功。&lt;/p&gt;  &lt;p&gt;「想改變未來，最好的時刻就是現在」，希望這個新的網誌能代表著全新的開始，以生理學的角度來看，由於新陳代謝的關係，其實每個人每一天都是一個全新的人。然而，許多人不知道的是，其實人這種生物(只是另一種大型哺乳類?)用了極多的能量幫自己作新陳代謝，這個與生俱來的生物特性給了我們很大的啟發，或許我們生來就是為了日新月異、不斷求新，人生不能停止學習，就在現在，踏出了第一步，也期望能持續到最後一步。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1910449797393154462-822629577857921238?l=luse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://luse.blogspot.com/feeds/822629577857921238/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post.html#comment-form' title='3 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/822629577857921238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1910449797393154462/posts/default/822629577857921238'/><link rel='alternate' type='text/html' href='http://luse.blogspot.com/2009/04/blog-post.html' title='新的開始'/><author><name>luse</name><uri>http://www.blogger.com/profile/18092821464398639243</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_UXivPAJu6Og/STP8jo1RUyI/AAAAAAAAAAM/YdFWWziuMAY/s1600-R/luse.jpg'/></author><thr:total>3</thr:total></entry></feed>
