[技術] Interrupted system calls

Written on 2:41 下午 by Yu Lai

這是最近寫程式遇到的,沒想到使用好好的UNIX Socket API居然會有爆走的時候。
在Survey了老半天,總算知道問題點所在。以下是看完The Linux GCC HOWTO的心得。

會發生Interrupted system calls主要是POSIX的系統檢查信號的次數,比起一些舊版的Unix是要多那麼一點。依照此規範,在Linux裡會在下列系統呼叫(system calls)的執行期間執行signal handlers: select(), pause(), connect(),accept(), read() on terminals, sockets, pipes or files in /proc, write() on terminals, sockets, pipes or the line printer, open() on FIFOs, PTYs or serial lines,ioctl() on terminals, fcntl() with command F_SETLKW, wait4(), syslog(), any TCP or NFS operations。而就其它的作業系統而言,你需要注意的可能就是下面這些系統呼叫(system calls)了: creat(), close(), getmsg(), putmsg(), msgrcv(), msgsnd(), recv(), send(), wait(), waitpid(), wait3(), tcdrain(), sigpause(), semop() to this list.

而問題點在於在系統呼叫(system calls)期間,若有一信號(那支程式本身應準備好handler因應了)產生,handler就會被呼叫。當handler將控制權轉移回系統呼叫時,它會偵測出它已經產生中斷,而且傳回值會立刻設定成-1,errno設定成EINTR。程式並沒有想到會發生這種事,所以就會bottles out了。

有兩種修正的方法可以選擇:

(1) 對每個你自行安裝(install)的signal handler,都須在sigaction旗號加上SA_RESTART。例如,把下列的程式,

     signal (sig_nr, my_signal_handler);

改成
     signal (sig_nr, my_signal_handler);
{ struct sigaction sa;
sigaction (sig_nr, (struct sigaction *)0, &sa);
#ifdef SA_RESTART
sa.sa_flags |= SA_RESTART;
#endif
#ifdef SA_INTERRUPT
sa.sa_flags &= ~ SA_INTERRUPT;
#endif
sigaction (sig_nr, &sa, (struct sigaction *)0);
}

要注意的是,當這部份的變更大量應用到系統呼叫之後,呼叫read(), write(),ioctl(), select(), pause() 與 connect()時,你仍然得自行檢查(check for)EINTR。如方法二所示。

(2) 你自己得很明確地(explicitly)檢查EINTR:

這裡有兩個針對read()與ioctl()的例子。

原始的程式片段,使用read()。
   int result;
while (len > 0) {
result = read(fd,buffer,len);
if (result <> 0) {
result = read(fd,buffer,len);
if (result < 0) { if (errno != EINTR) break; }
else { buffer += result; len -= result; }
}

原始的程式片段,使用ioctl()。
   int result;
result = ioctl(fd,cmd,addr);

修改成
   int result;
do { result = ioctl(fd,cmd,addr); }
while ((result == -1) && (errno == EINTR));

注意一點,有些版本的BSD Unix,其內定的行為(default behaviour)是重新執行系統呼叫。若要讓系統呼叫中斷,得使用SV_INTERRUPT或SA_INTERRUPT旗號。

另外補充一個由Richard Steven所提供給connect()的修改法。
   /* Start with fd just returned by socket(), blocking, SOCK_STREAM... */
while ( connect (fd, &name, namelen) == -1 && errno != EISCONN )
if ( errno != EINTR ) {
perror ("connect");
exit (EXIT_FAILURE);
}

[技術] 嵌入式linux下常見的文件系統

Written on 2:14 下午 by Yu Lai

嵌入式linux下常見的文件系統
• RomFS:只讀文件系統,可以放在ROM空間,也可以在系統的RAM中,嵌入式linux中常用來作根文件系統
• RamFS:利用VFS自身結構而形成的內存文件系統,使用系統的RAM空間
• JFFS/JFFS2:為Flash設計的日誌文件系統
• Yaffs:專門為Nand Flash設計
• proc:為Kernel和Module將Message發送給Process提供一種機制,可以查看系統Module裝載的信息
• devFS:設備文件系統

Linux上的Ext2fs
• 支持4 TB存儲、文件名稱最長1012字符
• 可選擇邏輯塊
• 快速符號鏈接
• Ext2不適合flash設備
• 是為象IDE 設備那樣的塊設備設計的,邏輯塊大小必須是512 byte、1 KB、2KB等
• 沒有提供對基於扇區的擦除/寫操作的良好管理
• 如果在一個扇區中擦除單個字節,必須將整個扇區複製到RAM,然後擦除,再重寫入
• 在出現電源故障時,Ext2fs 是不能防止崩潰的
• 文件系統不支持損耗平衡,縮短了flash的壽命

jffs/jffs2文件系統的優缺點
• 日誌文件系統
• 提供了更好的崩潰、掉電安全保護
• jffs2支持對flash的均勻磨損
• 在扇區級別上執行閃存擦除/寫/讀操作要比Ext2文件系統好
• 文件系統接近滿時,JFFS2 會大大放慢運行速度——垃圾收集

Nand上yaffs文件系統的優勢
• 專門為Nand flash設計的日誌文件系統
• jffs/jffs2不適合大容量的Nand flash
• jffs的日誌通過jffs_node建立在RAM中,佔用RAM空間:對於128MB的Nand大概需要4MB的空間來維護節點
• 啟動的時候需要掃瞄日誌節點,不適合大容量的Nand flash
• FAT系統沒有日誌編譯yaffs文件系統
• mtd的最新補丁升級?
• 接口更新,適合與yaffs
• 與原有的mtd驅動程序不兼容,需要重寫
• 如果使用舊mtd驅動需要定義Makefile中MTD_OLD = -DCONFIG_YAFFS_USE_OLD_MTD
• 參考文檔: yaffs-rootfs-howto
• 最新版的yaffs網站:
http://www.aleph1.co.uk/armlinux/projects/yaffs

使用yaffs文件系統
• 通過cat /proc/yaffs命令可以看到yaffs系統的相關信息
• mount -t yaffs /dev/mtdblock/0 /mnt/yaffs

[技術] malloc的效能

Written on 9:54 上午 by Yu Lai

轉載自強者學弟yuwen的個人板P_yuwen @ cd.twbbs.org

最近因為某些特殊需求,所以必須要使用rainbowtable來輔助,在FreeBSD的ports中有rainbowcrack可以使用,於是就裝了起來,順道測試一下之前抓到的幾個rainbowtable的效率。沒想到一執行rcrack就噴了 = =

只好苦命的來trace code,才發現一個白爛的問題,做這個ports的人在某個function內,因為找不到從linux對應的code,就直接把那段code拔掉 = = 所以return 值亂七八糟的,當然會噴segmentation fault!所以就自己補上那一小段code,本來以為這世界就會再度快樂的運轉。

當然事情不是那麼的簡單,跑沒多久又改噴bus error = = 經過再苦命的trace才找到bug,因為每個rainbowtable去跑一次需要allocate 1G的記憶體,而我拿來測的機器的記憶體只有1G,不過SWAP也有1.5G,不知道為啥就是不會成功。(謎! 板上有長輩知道嗎),所以又找了台有2G RAM的機器來測,發現一樣跑步起來,後來寫了個test code才發現似乎是FreeBSD的malloc的問題。同樣的code,在一樣只有1G的linux上面卻是可以成功。(FreeBSD的libc中的malloc是jemalloc,而linux上面的是ptmalloc2)

My test code:
unsigned char* a = (unsigned char *)malloc(sizeof(unsigned char)*1G);
if(a == NULL) printf("Damn\n");
else printf("Thanks God!\n");
Result:
FreeBSD 6.2-Stable + 1G RAM => Damn
FreeBSD 6.2-Stable + 3G RAM => Damn
Debian etch => Thanks God!

所以就開始尋找替代方案啦,幾個有名的malloc都拿來測試看看,包含下列幾種:
libumem(Sun的),libdlmalloc(FreeBSD開發者弄得),libtcmalloc (google出品)
結果不管換上哪一套,在本來的機器上都可以跑了!!!看來真的是malloc的問題。

解決問題後,就開始想測測看上面這幾套的效能,測試的code如下:
char a[65535];
跑 10 次
random填滿 a[65535], 每個malloc 1~64K
跑 1000000 次
random選一個a[x],如果不是null就free,如果null就
random malloc一塊 1~64K
end 跑
end 跑

結果: tcmalloc > dlmalloc >>>>>>>>>>>>>>>>>>> libumem ~= jemallo ~= ptmalloc
結論: 如果需要大量頻繁的malloc/free,每次都是small allocation的話,應該可以先試試看tcmalloc or dlmalloc

這又帶出一個想法,築夢每次晚間的load都非常重,看了一下code發現其實bbs會頻繁的malloc/free。也許cd.twbbs.org可以改用tcmalloc or dlmalloc看看,也許可以增加一點效率~(ptt是改用dietlibc的malloc)

--
可惜手邊沒有bbs的機器來測, 手好癢阿~ Orz

PS: 最後補一個相關的ref: http://plog.longwin.com.tw/news-unix/2007/03/29/mysql_tcmalloc_2007

[投資] 一些國家yahoo財經連結

Written on 4:40 下午 by Yu Lai

http://hk.finance.yahoo.com/ 香港
http://tw.finance.yahoo.com/ 台灣
http://cn.finance.yahoo.com/ 中國
http://finance.yahoo.com/ 美國
http://ca.finance.yahoo.com/ 加拿大
http://uk.finance.yahoo.com/ 英國
http://sg.finance.yahoo.com/ 新加坡
http://quote.yahoo.co.jp/ 日本
http://fr.finance.yahoo.com/ 法國
http://kr.finance.yahoo.com/ 韓國
http://au.finance.yahoo.com/ 紐澳
http://de.finance.yahoo.com/ 德國
http://it.finance.yahoo.com/ 義大利 nonono 維大力
http://es.finance.yahoo.com/ 西班牙
http://in.finance.yahoo.com/ 印度
http://mx.finance.yahoo.com/ 墨西哥
http://br.finance.yahoo.com/ 巴西
http://ar.finance.yahoo.com/ 阿根廷

[工具] 超好用網頁配色網站

Written on 11:03 上午 by Yu Lai

1、收錄了很多主題配色的方案
http://www.print100.com/hk/html/csi.aspx

2、選定你的主題色,輕輕一按就能幫你搭配合適的顏色,還能調節亮度與飽和度,非常專業喔
http://wellstyled.com/tools/colorscheme/index-en.html

3、只要輸入一個物品名稱(例如tree)就能產生相關顏色
http://colr.org/

4、選擇你的基調色,馬上給你16種配色
http://www.colorschemer.com/online.html

[閒聊] 直銷傳銷的十大謊言Ten Big Lies of Multilevel

Written on 1:27 上午 by Yu Lai

最近在新聞看到腦殘直銷的事,剛好又看到這篇文章,覺得還不錯,就轉來給大家看一下囉。

http://www.pjhuang.net/2007/04/ten-big-lies-of-multilevel-marketing.html

1.謊言:直銷傳銷提供比傳統的商業模式更好的機會賺取鉅額的金錢。
事實:幾乎對所有投入直銷傳銷的人而言,參與直銷傳銷計畫都有不好的財務結果,只有少於1%的人勉強能獲利,而能有相當獲利的人比例更低。

2.謊言:經由人際關係網路銷售是最流行與有效的銷售方式,消費者喜歡直銷傳銷的一對一經驗。
事實:個人零售已經過時,更不是未來的趨勢,網際網路才是未來趨勢。向朋友直接購買商品,往往讓消費者選擇變少,價格偏高,購買不方便,又和朋友有令人不習慣的商業關係。事實上,直銷傳銷靠的是不斷重複販賣一種可能賺錢的契約給下一個經銷商。

3.謊言:未來所有零售商品都會以直銷方式販售,零售商與百貨公司都會被直銷傳銷打敗而消失。
事實:直銷傳銷的祇佔所有零售量的不到1%,和正常零售量根本無法比較,而且這些銷量中,大半還是新會員加入時必須購買的額度。買產品的人多是為了買一個致富的彩票,除了你自己的銷售努力還有你下線的銷售努力,可以讓你致富。合法的直銷傳銷業就像合法的賭博業,只是你的勝率比賭博還低一點。投資股市也很像賭博,不過你的勝率可能高一點。

4.謊言:直銷傳銷將給你幸福快樂的人生,讓你可以成就所有你想完成的事。
事實:在直銷傳銷的招募會議中展示的往往是物質主義的極致,財富前100大公司的職員前景可能都比不上直銷傳銷所描繪的財富與奢華。但是這和一般人希望的幸福其實是衝突的,一般人多半只想在自己有天份與興趣的項目上獲得成就感。

5.謊言:直銷傳銷是神聖的運動。
事實:用貧窮感與心想事成的精神力來推廣直銷,把直銷傳銷比喻成傳教,甚至將銷售團隊自稱為團契,是對宗教的曲解,那些將希望與夢想建築在財富上的人,完全搞錯了宗教的意義。直銷傳銷團體所提供的歸屬感與支援,都是完全建立在購買的基礎之上,一旦你不再購買與招募新人,這個團體就什麼也沒了。

6.謊言:直銷傳銷很容易成功,你的親人與朋友是最自然的下線,他們將是你一生的顧客。
事實:想在家族與友情中獲取上商業利益,往往是不智的,直銷傳銷的出現,對社區與個人的生活都有很不好的影響,人就是不喜歡因情份的關係而買東西。想利用社交關係來獲利,往往會毀了你的社交生活。這我也在顧問的業務推廣上,發現有類似的現象,我的付費諮詢顧客,多來自家族與朋友圈之外。

7.謊言:你可以利用閒暇時間做直銷傳銷,它提供彈性與自由,每週幾小時就能賺得不錯的兼差收入,並且收入可能成長到可以辭去原本的工作,獲得財務自由。
事實:想從直銷傳銷賺錢需要投入很多時間,更需要人際關係技巧與堅持的毅力。此外,直銷傳銷模式比一般工作入侵更多你的生活與時間,每個人都可能是下線,每分鐘都在行銷。直銷傳銷模式以能幫助你財務獨立、財務自由為誘因,結果剛好相反,很多直銷人之所以如此沉迷與依賴直銷傳銷,是因為他們已經失去了所有其他正常的社交關係,只剩下直銷傳銷關係,讓他們對僅存的直銷傳銷關係,變得更為依賴。這是病態的人生,而非幸福的人生。

8.謊言:直銷傳銷是個正向積極、彼此支援的新商業模式,直銷傳銷模式彰顯了人道精神與個人自由。
事實:直銷傳銷的推廣者,往往是利用恐懼來招募新人,強調企業的冷酷無情,為別人工作是多麼的了無希望,其他工作都不能提供未來無限的收入,直銷傳銷被形容成你最後唯一快速輕鬆致富成功的機會。一個好的商業模式不該是需要憑藉預測別人的失敗與警告別人來招募。

9.謊言:直銷傳銷是擁有自己的事業最好的方式,讓你達到財富自由。
事實:參加直銷傳銷不是擁有自己的事業,創建直銷公司才是,多數參加直銷者,不能自由銷售其他公司的商品,而且公司可以很容易地中止參加直銷者的資格,下線也可能被無預警移除,參加直銷者必須嚴格遵守公司經營規則,只能複製公司設定的模式,既不獨立也不自由,參加直銷者不算是事業主,只是參與簽署複雜合約的一方,而且對這份合約,參加直銷者沒有任何置啄與修改的控制權利。

10.謊言:直銷傳銷不是老鼠會(pyramid scheme),而是真的有銷售商品。
事實:有銷售商品並不表示就一定不是老鼠會騙局,在美國的法律,區分老鼠會詐欺與合法的直銷傳銷行為,是需要法院判決的。而多數目前存在的直銷傳銷公司並不表示就一定合法,只是尚未被起訴與接受法院判決過。目前美國法院判決的原則是要有70%以上的商品銷售是賣給非經銷商的最終顧客,才是合法的直銷傳銷行為。而多數直銷傳銷公司只有18%的商品銷售是賣給非經銷商。

本文翻譯摘錄自http://www.mlmwatch.org/01General/10lies.html
是某天看了一篇提到MLM的文章後,想要研究MLM的獎勵制度設計理念時,搜尋的結果。讀來感覺提綱挈領,作者英文論理十分清楚,發覺過去沒有人中文翻譯過,所以試著摘譯,加上一點改寫。希望對尋找財富自由的人有參考的價值。

網路MLM直銷算不算是老鼠會騙局呢?首先要看參與網路MLM直銷者,是否需要先付出一筆錢購買這個獲利機會的契約?通常要,否則MLM就玩不起來了。而網路MLM直銷的商品或利潤有多少百分比來自非參與直銷者的貢獻。

網路MLM直銷就像一般直銷,可能會毀了你的網路社交生活,讓你的網路社交只剩下網路MLM直銷社群,相濡以沫。網路MLM直銷的優點是也許你能區別現實生活與網路生活,而繼續保有現實生活中的社交生活圈。

[技術] 主機板的叫聲

Written on 10:56 上午 by Yu Lai

1. AMI BIOS
 1短——記憶體重新整理失敗
 2短——記憶體ECC校驗錯誤
 3短——系統基本記憶體(第1個64K)檢查失敗
 4短——系統時鐘出錯
 5短——中央處理器(CPU)錯誤
 6短——鍵盤控制器錯誤
 7短——系統真實模式錯誤,不能切換到保護模式
 8短——顯示記憶體錯誤(顯示記憶體可能有所損壞)
 9短——ROM BIOS檢驗和錯誤
 1長3短——記憶體錯誤(記憶體損壞,請更換)
 1長8短——顯示測試錯誤(顯示器資料線鬆動或顯示卡沒插穩)
2. Award BIOS
 1短——系統正常啟動
 2短——一般錯誤,請進入CMOS SETUP重新設置不正確的選項
 1長1短——RAM或主機板出錯
 1長2短——顯示錯誤(顯示器或顯示卡)
 1長3短——鍵盤控制器錯誤
 1長9短——主機板FlashRAM或EPROM錯誤(BIOS損壞)
 不斷地響(長聲)——記憶體沒插穩或損壞
 不停地響——電源、顯示器和顯示卡沒有連接好
 重複短響——電源故障
 無聲音無顯示——電源故障

3. Phoenix BIOS
 1短——系統正常啟動
 3短——系統加電自檢啟始化(POST)失敗
 1短1短2短——主機板錯誤(主機板損壞,請更換)
 1短1短3短——主機板電池沒電或CMOS損壞
 1短1短4短——ROM BIOS校驗出錯
 1短2短1短——系統實時時鐘有問題
 1短2短2短——DMA通道啟始化失敗
 1短2短3短——DMA通道頁寄存器出錯
 1短3短1短——記憶體通道重新整理錯誤(問題範圍為所有的記憶體)
 1短3短2短——基本記憶體出錯(記憶體損壞或RAS設置錯誤)
 1短3短3短——基本記憶體出錯(很可能是DIMM槽上的記憶體損壞)
 1短4短1短——基本記憶體某一地址出錯
 1短4短2短——系統基本記憶體(第1個64K)有奇偶校驗錯誤
 1短4短3短——EISA總線時序器錯誤
 1短4短4短——EISA NMI口錯誤
 2短1短1短——系統基本記憶體(第1個64K)檢查失敗
 3短1短1短——第1個DMA控制器或寄存器出錯
 3短1短2短——第2個DMA控制器或寄存器出錯
 3短1短3短——主中斷處理寄存器錯誤
 3短1短4短——副中斷處理寄存器錯誤
 3短2短4短——鍵盤時鐘有問題,在CMOS中重新設置成Not installed來跳過POST
 3短3短4短——顯示卡RAM出錯或無RAM,不屬於致命錯誤
 3短4短2短——顯示器資料線鬆了或顯示卡沒插穩或顯示卡損壞
 3短4短3短——未發現顯示卡的ROM BIOS
 4短2短1短——系統實時時鐘錯誤
 4短2短3短——鍵盤控制器(8042)中的Gate A20開關有錯,BIOS不能切換到保護模式
 4短2短4短——保護模式中斷錯誤
 4短3短1短——記憶體錯誤(記憶體損壞或RAS設置錯誤)
 4短3短3短——系統第二時鐘錯誤
 4短3短4短——實時時鐘錯誤
 4短4短1短——串行口(COM口、滑鼠口)故障
 4短4短2短——並行口(LPT口、列印口)錯誤
 4短4短3短——數學協處理器(8087、80287、80387、80487)出錯

[閒聊] 適合coding的字型(font)

Written on 10:28 下午 by Yu Lai

http://www.proggyfonts.com/

其實也算分享啦,用起來版面給人很整齊舒適的感覺,
字母和符號有特別設計過,置中、固定寬度,但是只有one size。
作者說專門設計給code listing,偶爾換換字型也不錯^__^。

[閒聊] 淺談兔子是怎樣吃掉狼的

Written on 12:25 上午 by Yu Lai

一隻兔子在山洞前寫文章,一隻狼走了過來,問:

“兔子啊,你在幹什麼?”

兔子答曰:“寫文章。”

狼問:“ 什麼題目?”

兔子答曰:“《淺談兔子是怎樣吃掉狼的》。”

狼哈哈大笑,表示不信,於是兔子把狼領進山洞。

過了一會,兔子獨自走出山洞,繼續寫文章。

一隻野豬走了過來,問:“兔子你在寫什麼?”

兔子答:“文章。”

野豬問:“題目是什麼?”

兔子答:“《淺談兔子是如何把野豬吃掉的》。”

野豬不信,於是同樣的事情發生。

最後,在山洞裡,一隻獅子在一堆白骨之間,

滿意的剔著牙讀著兔子交給它的文章:

題目是:“《一隻動物,能力大小關鍵要看你的老闆是誰》。



這隻兔子有次不小心告訴了他的一個兔子朋友,

這消息逐漸在森林中傳播;

獅子知道後非常生氣,他告訴兔子:

“如果這個星期沒有食物進洞,我就吃你。”

於是兔子繼續在洞口寫文章

一隻小鹿走過來,“兔子,你在幹什麼啊?”

兔子答:“寫文章”

小鹿問:“什麼題目?”

兔子答曰:“《淺談兔子是怎樣吃掉狼的》。”

“哈哈,這個事情全森林都知道啊,你別胡弄我了,

我是不會進洞的”

“我馬上要退休了,獅子說要找個人頂替我,

難道你不想這篇文章的兔子變成小鹿麼?”

小鹿想了想,終於忍不住誘惑,跟隨兔子走進洞裡。

過了一會,兔子獨自走出山洞,繼續寫文章

一隻小馬走過來,同樣是事情發生了。

最後,在山洞裡,一隻獅子在一堆白骨之間,

滿意的剔著牙讀著兔子交給它的文章:

題目是:《如何發展下線動物為老闆提供食物》



隨著時間的推移,獅子越長越大,

兔子的食物已遠遠不能填飽肚子。

一日,他告訴兔子:“我的食物量要加倍,例如:

原來4天一隻小鹿,現在要2天一隻,

如果一周之內改變不了局面,我就吃你。

於是,兔子離開洞口,跑進森林深處,他見到一隻狼

“你相信兔子能輕鬆吃掉狼嗎”

狼哈哈大笑,表示不信,於是兔子把狼領進山洞。

過了一會,兔子獨自走出山洞,繼續進入森林深處

這回他碰到一隻野豬----“你相信兔子能輕鬆吃掉野豬嗎”

野豬不信,於是同樣的事情發生了。

原來森林深處的動物並不知道兔子和獅子的故事

最後,在山洞裡,一隻獅子在一堆白骨之間,

滿意的剔著牙讀著兔子交給它的文章

題目是:《如何實現由坐商到行商的轉型為老闆提供更多的食物》



時間飛快,轉眼之間,兔子在森林裡的名氣越來越大

因為大家都知道它有一個很厲害的老闆

這只小兔開始橫行霸道,欺上欺下,沒有動物敢惹牠

兔子時時想起和烏龜賽跑的羞辱

牠找到烏龜說:“ 三天之內,見我老闆!”揚長而去

烏龜難過的哭了

這時卻碰到了一位獵人,烏龜把這事告訴了他

獵人哈哈大笑

於是森林裡發生了一件重大事情

獵人披著獅子皮和烏龜一起在吃兔子火鍋

地下丟了半張紙片歪歪扭扭的寫著:

《山外青山樓外樓,強中還有強中手啊》!!



在很長一段時間裡森林裡恢復了往日的寧靜,

兔子吃狼的故事似乎快要被大家忘記了

不過一隻年輕的老虎在聽說了這個故事後,

被激發了靈感於是他抓住了一隻羚羊,對羚羊說,

如果你可以像以前的兔子那樣為我帶來食物那我就不吃你。

於是,羚羊無奈的答應了老虎,而老虎也悠然自得的進了山洞。

可是三天過去了,也沒有見羚羊領一隻動物進洞。

他實在憋不住了,想出來看看情況。

羚羊早已不在了,他異常憤怒。

正在他暴跳如雷的時候突然發現了羚羊寫的一篇文章

題目是:《想要做好老闆先要懂得怎樣留住員工》

[技術] Linux具名管線(FIFOs)編程

Written on 8:39 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/16/1531201.aspx

具名管線可以用於任何兩個程序間通信,因為有名字可引用。注意管道都是單向的,因此雙方通信需要兩個管道。

下面分別是這兩個程式代碼,同樣是Lucy先運行,然後是Peter。

fifoLucy.c

#include <sys/types.h>
#include <sys/stat.h>

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
char write_fifo_name[] = "lucy";
char read_fifo_name[] = "peter";
int write_fd, read_fd;
char buf[256];
int len;
struct stat stat_buf;

int ret = mkfifo(write_fifo_name, S_IRUSR | S_IWUSR);
if( ret == -1)
{
printf("Fail to create FIFO %s: %s",write_fifo_name,strerror(errno));
exit(-1);
}

write_fd = open(write_fifo_name, O_WRONLY);
if(write_fd == -1)
{
printf("Fail to open FIFO %s: %s",write_fifo_name,strerror(errno));
exit(-1);
}

while((read_fd = open(read_fifo_name,O_RDONLY)) == -1)
{
sleep(1);
}

while(1)
{
printf("Lucy: ");
fgets(buf, 256, stdin);
buf[strlen(buf)-1] = '\0';
if(strncmp(buf,"quit", 4) == 0)
{
close(write_fd);
unlink(write_fifo_name);
close(read_fd);
exit(0);
}
write(write_fd, buf, strlen(buf));
len = read(read_fd, buf, 256);
if( len > 0)
{
buf[len] = '\0';
printf("Peter: %s\n", buf);
}
}
}
fifoPeter.c
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>

int main(void)
{
char write_fifo_name[] = "peter";
char read_fifo_name[] = "lucy";
int write_fd, read_fd;
char buf[256];
int len;

int ret = mkfifo(write_fifo_name, S_IRUSR | S_IWUSR);
if( ret == -1)
{
printf("Fail to create FIFO %s: %s",write_fifo_name,strerror(errno));
exit(-1);
}

while((read_fd = open(read_fifo_name, O_RDONLY)) == -1)
{
sleep(1);
}

write_fd = open(write_fifo_name, O_WRONLY);
if(write_fd == -1)
{
printf("Fail to open FIFO %s: %s", write_fifo_name, strerror(errno));
exit(-1);
}

while(1)
{
len = read(read_fd, buf, 256);
if(len > 0)
{
buf[len] = '\0';
printf("Lucy: %s\n",buf);
}
printf("Peter: ");
fgets(buf, 256, stdin);
buf[strlen(buf)-1] = '\0';
if(strncmp(buf,"quit", 4) == 0)
{
close(write_fd);
unlink(write_fifo_name);
close(read_fd);
exit(0);
}
write(write_fd, buf, strlen(buf));
}
}

[技術] Linux Semaphores PV操作程式

Written on 8:36 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/16/1531189.aspx

多程序實現PV操作,子程序為生產者,倉庫可以容納5個物品,每次檢查倉庫是否滿,若不滿則執行P操作,生產一個物品放入倉庫。父程序檢驗倉庫是否空,若不空則消耗一個物品,執行V操作。sleep函數是隨即設置生產或消費所用時間。

程式主要目的是展示Semaphores的使用,以備以後參考。

#include <stdio.h>
#include <stdlib.h>

#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>

int main()
{
int semID,ret,num;
int sleepTime;
struct sembuf buf;

semID = semget(IPC_PRIVATE,1,0666);
if(semID < 0)
{
printf("建立Semaphores出錯\n");
exit(1);
}

if(fork() == 0)
{
while(1)
{
num = semctl(semID,0,GETVAL,NULL);
if(num < 5)
{
sleepTime = rand() % 5;
sleep(sleepTime);

buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = 0;

ret = semop(semID,&buf,1);
if(ret < 0)
{
printf("執行P操作失敗!\n");
exit(1);
}

num = semctl(semID,0,GETVAL,NULL);
printf("生產者生產了一個物品放入了倉庫...倉庫中現有物品%d個\n",num);
}
}
}
else
{
while(1)
{
num = semctl(semID,0,GETVAL,NULL);
if(num > 0)
{
sleepTime = rand() % 5;
sleep(sleepTime);

buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = 0;

ret = semop(semID,&buf,1);
if(ret < 0)
{
printf("執行V操作失敗!\n");
exit(1);
}

num = semctl(semID,0,GETVAL,NULL);
printf("消費者從倉庫中消耗了一個物品...倉庫中現有物品%d個\n",num);
}
}
}

return 0;
}

[技術] Linux Message Queue程式

Written on 8:23 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/16/1531183.aspx

檔案msg為空檔案,可以為任何內容,這裡只是為了ftok函數使用。程式透過建立Message Queue,完成程序間通信,注意msgrcv的第四個參數為Message Type,他定義了從Queue中取得Message的Type。

下面是msgLucy.c,是建立Message Queue的

#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>

#define PROJID 0xFF
#define LUCY 1
#define PETER 2

int mqid;

void terminate_handler(int signo)
{
msgctl(mqid,IPC_RMID,NULL);
exit(0);
}

int main()
{
char filenm[] = "msg";
key_t mqkey;
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[256]; /* message data */
}msg;
int ret;

mqkey = ftok(filenm,PROJID);
if(mqkey == -1)
{
perror("ftok error: ");
exit(-1);
}

mqid = msgget(mqkey,IPC_CREAT | IPC_EXCL | 0666);
if(mqid == -1)
{
perror("msgget error: ");
exit(-1);
}

signal(SIGINT, terminate_handler);
signal(SIGTERM, terminate_handler);

while(1)
{
printf("Lucy: ");
fgets(msg.mtext, 256, stdin);
if (strncmp("quit", msg.mtext, 4) == 0)
{
msgctl(mqid,IPC_RMID,NULL);
exit(0);
}
msg.mtext[strlen(msg.mtext)-1] = '\0';
msg.mtype = LUCY;
msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
msgrcv(mqid,&msg,256,PETER,0);
printf("Peter: %s\n", msg.mtext);
}
}
下面的是msgPeter,是和Lucy通信的,程式先執行Lucy,再執行Peter
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>

#define PROJID 0xFF
#define LUCY 1
#define PETER 2

int main()
{
char filenm[] = "msg";
int mqid;
key_t mqkey;
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[256]; /* message data */
}msg;
int ret;

mqkey = ftok(filenm, PROJID);
if(mqkey == -1)
{
perror("ftok error: ");
exit(-1);
}

mqid = msgget(mqkey, 0);
if(mqid == -1)
{
perror("msgget error: ");
exit(-1);
}

while(1)
{
msgrcv(mqid,&msg,256,LUCY,0);
printf("Lucy: %s\n",msg.mtext);
printf("Peter: ");
fgets(msg.mtext,256,stdin);
if(strncmp("quit", msg.mtext, 4) == 0)
{
exit(0);
}
msg.mtext[strlen(msg.mtext)-1] = '\0';
msg.mtype = PETER;
msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
}
}

[技術] Linux多執行緒結束控制程式

Written on 8:20 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/05/1521170.aspx

pthread_cleanup_push和pthread_cleanup_pop是一對的。push中第一個參數是函數,第二個參數是函數的參數,pop為0表示彈出時不執行,否則再執行到pop時會執行push中指定的函數。在他們之間的程序段如果退出則執行push中指定的函數。
代碼如下:

#include <stdio.h>
#include <pthread.h>

void display()
{
printf("Hello lcrystal!\n");
}

void fun()
{
pthread_cleanup_push((void *)display,NULL);

pthread_exit(0);

pthread_cleanup_pop(0);
}

int main()
{
pthread_t id;

pthread_create(&id,NULL,(void *)fun,NULL);
pthread_join(id,NULL);

return 0;
}

[技術] Linux管線(pipe)程式

Written on 8:07 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/05/1521168.aspx

管線只能用於父子程序間通信,pipe函式用一個int[]作參數,int[] fd中fd[0]用於讀,fd[1]用於寫。代碼如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

int main()
{
int pipe1_fd[2], pipe2_fd[2];

char * parent_talks[] = {"Hi, my baby","Can you tell daddy date and time?","Daddy have to leave here, bye!",NULL};
char * child_talks[] = {"Hi, daddy","Sure,","Bye!",NULL};

char parent_buf[256], child_buf[256];
char * parent_ptr, * child_ptr;

int i,j, len;
int child_status;
time_t curtime;

if(pipe(pipe1_fd) < 0)
{
printf("pipe1 create error\n");
return -1;
}

if(pipe(pipe2_fd) < 0)
{
printf("pipe2 create error\n");
return -1;
}

if(fork() == 0) //child
{
//pipe1_fd[0] is used to read, pipe2_fd[1] is used to write
close(pipe1_fd[1]);
close(pipe2_fd[0]);

i = 0;
child_ptr = child_talks[i];
while(child_ptr != NULL)
{
len = read(pipe1_fd[0],child_buf,255);
child_buf[len] = '\0';
printf("Parent: %s\n",child_buf);

if(i == 1)
{
time(&curtime);
len = sprintf(child_buf, "%s %s", child_ptr, ctime(&curtime));
child_buf[len-1] = '\0';
write(pipe2_fd[1],child_buf,strlen(child_buf));
}
else
{
write(pipe2_fd[1],child_ptr,strlen(child_ptr));
}

child_ptr = child_talks[++i];
}

close(pipe1_fd[0]);
close(pipe2_fd[1]);
exit(0);
}
else //parent
{
//pipe1_fd[1] is used to write, pipe2_fd[0] is used to read
close(pipe1_fd[0]);
close(pipe2_fd[1]);

j = 0;
parent_ptr = parent_talks[j];
while(parent_ptr != NULL)
{
write(pipe1_fd[1],parent_ptr,strlen(parent_ptr));
len = read(pipe2_fd[0],parent_buf,255);
parent_buf[len] = '\0';
printf("Child: %s\n", parent_buf);
parent_ptr = parent_talks[++j];
}

close(pipe1_fd[1]);
close(pipe2_fd[0]);
wait(&child_status);
exit(0);
}
}

[技術] Linux攔截Signal程式

Written on 7:57 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/05/1521159.aspx

攔截鍵盤Ctrl + C,程式執行後,進入while(1)迴圈,當按下Ctrl + C後,執行signal函數指定的函式fun,輸出字串。
代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void fun(int signal)
{
printf("Hello World!\n");
exit(0);
}

int main()
{
signal(SIGINT,fun);

while(1)
{
;
}
}

[技術] Linux Shared Memory程式

Written on 7:40 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/05/1521158.aspx

要運行程式,需要在當前目錄下建立一個share檔案,share是一個空檔案,沒有任何意義,只是函數ftok需要一個檔案名作參數,ftok另一個參數可以為任何數字。

程式執行後,分為父子程序,子程序申請Shared Memory,然後等待父程序繼續執行,父程序首先等待子程序申請到Shared Memory區段(segment),然後輸出Shared Memory中的內容,為了演示Shared Memory可以隨時更新,程序中在子程序裡產生隨機數寫入Shared Memory供父程序讀取。
代碼如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <time.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

int shmID;
char * buf;

void finalize(int signo)
{
shmctl(shmID,IPC_RMID,NULL);

exit(0);
}

int main()
{
int i = 1;
key_t shmKey;

signal(SIGINT,finalize);
signal(SIGTERM,finalize);

if(fork() == 0) //子程序
{
shmKey = ftok("share",16); //可以使用任何大於0的數字,如果名字和數相同,則產生的key相同。
if(shmKey == -1)
{
printf("建立key出錯\n");
exit(-1);
}

shmID = shmget(shmKey,20,IPC_CREAT | IPC_EXCL | 0666);
if(shmID == -1)
{
printf("建立Shared Memory Segment出錯\n");
exit(-1);
}

sleep(2); //等待父程序執行,好顯示第一行為空。
while(1)
{
buf = (char *)shmat(shmID,NULL,0);
srandom(time(NULL));
sprintf(buf,"%d",random());
shmdt(buf);
}
}
else //父程序
{
sleep(1); //讓子程序先執行,以建立Shared Memory Segment。

shmKey = ftok("share",16); //可以使用任何大於0的數字,如果名字和數相同,則產生的key相同。
if(shmKey == -1)
{
printf("建立key出錯\n");
exit(-1);
}

shmID = shmget(shmKey,20,0); //0表示如果shmKey映射的不存在則報錯。
if(shmID == -1)
{
printf("引用Segment id出錯\n");
exit(-1);
}

while(1)
{
buf = (char *)shmat(shmID,NULL,0);
printf("%d. 現在Shared Memory中的內容為:%s\n",i++,buf);
shmdt(buf);
sleep(1);
}
}

return 0;
}

[技術] Linux多執行緒程式

Written on 7:20 下午 by Yu Lai

整個程式並沒有體現出對共享資源鎖的保護使用,只是個簡單的例子,原理完全正確,但由於簡單,CPU運行一定會順利的執行,因此不加鎖結果也相同。主要目的是演示如何建立執行緒,如何建立mutex實現共享鎖。代碼如下:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>

int resource = 0;
pthread_mutex_t mutex;

void handle()
{
int i;

for(i = 0; i < 10; i++)
{
pthread_mutex_lock(&mutex);

resource++;

pthread_mutex_unlock(&mutex);
sleep(1);
}
}

int main()
{
pthread_t id;
int i ;
int ret;

ret = pthread_mutex_init(&mutex,NULL);
if(ret != 0)
{
printf("創建共享鎖失敗!\n");
exit(1);
}

ret = pthread_create(&id,NULL,(void *)handle,NULL);
if(ret != 0)
{
printf("創建線程出錯!\n");
exit(1);
}

for(i = 0; i < 10; i++)
{
pthread_mutex_lock(&mutex);

printf("%d\n",resource);

pthread_mutex_unlock(&mutex);
sleep(2);
}

pthread_join(id,NULL);
pthread_mutex_destroy(&mutex);

return (0);
}

[技術] 使用AutoMake輕鬆生成Makefile

Written on 5:31 下午 by Yu Lai

From: http://www.itepub.net/html/kaifawendang/caozuoxitong/Linux/bianchengkaifa/2006/0501/14712.html

摘要:
在 Unix 上寫過程序的人一般都遇到過 Makefile,尤其是用 C 來開發程序的人。用 make 來開發和編譯程序的確很方便,可是要寫出一個MakeFile就不那麼簡單了。偏偏介紹 Makefile 的文件不多,GNU Make 那份印出來要幾百頁的文件,光看完 Overview 自己就快要先Over了,難怪許多人聞 Unix色變。本文將介紹如何利用 GNU Autoconf 及 Automake 這兩套軟件來幫助『自動』產生 Makefile 文件,並且讓開發出來的的軟件可以像 Apache, MySQL 和常見的 GNU 軟件一樣,只要會 ``./configure'', ``make'', ``make install'' 就可以把程序安裝到系統中。如果您有心開發 Open Source 的軟件,或只是想在 Unix 系統下寫寫程序。希望這份介紹文件能幫助您輕鬆的進入 Unix Programming 的殿堂。


1. 簡介
Makefile 基本上就是『目標』(target), 『關聯』(dependencies) 和『動作』三者所組成的一系列規則。而 make 就會根據 Makefile 的規則來決定如何編譯 (compile) 和連接 (link) 程式。實際上,make 可做的不只是編譯和連接程序,例如 FreeBSD 的 port collection 中,Makefile還可以做到自動下載遠程程序,解壓縮 (extract) , 打補丁 (patch),設定,然後編譯,安裝到系統中。
Makefile 基本結構雖然很簡單,但是妥善運用這些規則就可以變換出許多不同的花樣。卻也因為這樣,許多剛剛開始學習寫Makefile 時會覺得沒有規範可以遵循,每個人寫出來的Makefile都不大一樣,不知道從哪裡下手,而且常常會受到自己的開發環境的限制,只要環境參數不同或者路徑更改,可能 Makefile 就得跟著修改修改。雖然有 GNU Makefile Conventions (GNU Makefile慣例例)訂出一些使用 GNU 程式設計時撰寫 Makefile 的一些標準和規範,但是內容很長而且很複雜,並且經常作一些調整,為了減輕程序開發人員維護Makefile 的負擔,因此出現了Automake。
程序設計者只需要寫一些預先定義好的巨集 (macro),提交給Automake處理後會產生一個可以供 Autoconf 使用的 Makefile.in文件。再配合利用 Autoconf產生的自動培植設置文件 configure 即可產生一份符合符合 GNU Makefile 慣例的 Makeifle 了。

2. 上路之前
在開始使用 Automake 之前,首先確認你的系統安裝有如下軟件:

1. GNU Automake
2. GNU Autoconf
3. GNU m4
4. perl
5. GNU Libtool (如果你需要產生 shared library)
建議最好也使用 GNU C/C++ 編譯器 、GNU Make 以及其它 GNU 的工具程序來作為開發的環境,這些工具都是屬於 Open Source Software 不但免費而且功能強大。如果你是使用 Red Hat Linux 可以找到所有上述軟件的 rpm 文件,FreeBSD 也有現成的 package 可以直接安裝,或也可以自行下載這些軟件的源代碼回來安裝。下面的示例是在Red Hat Linux 5.2 + CLE2 的環境下所完成的。

3. 一個簡單的例子 Automake 所產生的 Makefile 除了可以做到程式的編譯和連接,也已經把如何產生程序文件 (如 manual page, info 文件及 dvi 文件) 的動作,還有把源碼文件包裝起來以供發佈都考慮進去了,所以程序源代碼所存放的目錄結構最好符合GNU 的標準慣例,接下來就用一個hello.c 來做為例子。
在工作目錄下建立一個新的子目錄"devel"',再在 devel 下建立一個"hello"' 的子目錄,這個目錄將作為存放 hello這個程序及其相關文件的地方:
% mkdir devel
% cd devel
% mkdir hello
% cd hello 用編輯器寫一個hello.c文件,

#include
int main(int argc, char** argv) {
printf(``Hello, GNU!n'');
return 0;
}

接下來就要用 Autoconf 及 Automake 來產生 Makefile 文件了,
1. 用 autoscan 產生一個 configure.in 的原型,執行autoscan 後會產生一個configure.scan 的文件,可以用它作為 configure.in文件的藍本。
% autoscan
% ls configure.scan hello.c

2 . 編輯 configure.scan文件,如下所示,並且改名為configure.in
dnl Process this file with autoconf to produce a configure script.
AC_INIT(hello.c)
AM_INIT_AUTOMAKE(hello, 1.0)
dnl Checks for programs.
AC_PROG_CC
dnl Checks for libraries.
dnl Checks for header files.
dnl Checks for typedefs, structures, and compiler characteristics.
dnl Checks for library functions.
AC_OUTPUT(Makefile)

3. 執行 aclocal 和 autoconf ,分別會產生 aclocal.m4 及 configure 兩個文件
% aclocal
% autoconf
% ls aclocal.m4 configure configure.in hello.c

4. 編輯 Makefile.am 文件,內容如下
AUTOMAKE_OPTIONS= foreign
bin_PROGRAMS= hello
hello_SOURCES= hello.c

5. 執行 automake --add-missing ,Automake 會根據Makefile.am 文件產生一些文件,包含最重要的 Makefile.in
% automake --add-missing automake: configure.in: installing `./install-sh' automake: configure.in: installing `./mkinstalldirs' automake: configure.in: installing `./missing'

6. 最後執行 ./configure ,
% ./configure creating cache ./config.cache checking for a BSD compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking whether make sets ${MAKE}... yes checking for working aclocal... found checking for working autoconf... found checking for working automake... found checking for working autoheader... found checking for working makeinfo... found checking for gcc... gcc checking whether the C compiler (gcc ) works... yes checking whether the C compiler (gcc ) is a cross-compiler... no checking whether we are using GNU C... yes checking whether gcc accepts -g... yes updating cache ./config.cache creating ./config.status creating Makefile

現在你的目錄下已經產生了一個 Makefile 檔,下個 ``make'' 指令就可以開始編譯 hello.c 成執行檔,執行 ./hello 和 GNU 打聲招呼吧!
% make gcc -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -c hello.c gcc -g -O2 -o hello hello.o 
% ./hello Hello! GNU!

你還可以試試 ``make clean'',''make install'',''make dist'' 看看會有什麼結果。你也可以把產生出來的 Makefile 秀給你的老闆,讓他從此對你刮目相看 :-)

4. 追根問底
上述產生Makefile 的過程和以往自行編寫的方式非常不一樣,捨棄傳統自定義make 的規則,使用 Automake 只需用到一些已經定義好的巨集就可以了。我們把巨集及目標 (target)寫在Makefile.am 文件內,Automake 讀入 Makefile.am 文件後會把這一串已經定義好的巨集展開並產生相對應的 Makefile.in 文件,然後再由 configure這個 shell script 根據 Makefile.in 產生合適的Makefile。
[Figure 1:利用 autoconf 及 automake產生Makefile 的流程]
上圖表示在上一範例中要使用的文件檔案及產生出來的文件,有星號 (*) 者代表可執行文件。在此示例中可由 Autoconf 及 Automake 工具所產生的額外文件有 configure.scan、aclocal.m4、configure、Makefile.in,需要自行加入設置的有configure.in 及 Makefile.am。
4.1 編輯 configure.in 文件 Autoconf 是用來產生 'configure'文件的工具。'configure' 是一個 shell script,它可以自動設定原始程序以符合各種不同平台上Unix 系統的特性,並且根據系統參數及環境產生合適的Makefile文件或C 的頭文件(header file),讓原始程式可以很方便地在不同的平台上進行編譯。Autoconf會讀取 configure.in 文件然後產生'configure' 這個 shell script。
configure.in 文件內容是一系列GNU m4 的巨集,這些巨集經autoconf處理後會變成檢查系統特性的shell scripts。 configure.in 內巨集的順序並沒有特別的規定,但是每一個configure.in 文件必須在所有巨集前加入 AC_INIT 巨集,然後在所有巨集的最後加上 AC_OUTPUT 巨集。可先用 autoscan 掃瞄原始文件以產生一個 configure.scan 文件,再對 configure.scan 做些修改成 configure.in 文件。在範例中所用到的巨集如下:
dnl 這個巨集後面的字不會被處理,可以視為註釋 AC_INIT(FILE) 該巨集用來檢查源代碼所在路徑,autoscan 會自動產生,一般無須修改它。 AM_INIT_AUTOMAKE(PACKAGE,VERSION) 這個是使用 Automake 所必備的巨集,PACKAGE 是所要產生軟件套件的名稱,VERSION 是版本編號。 AC_PROG_CC 檢查系統可用的C編譯器,若源代碼是用C寫的就需要這個巨集。 AC_OUTPUT(FILE) 設置 configure 所要產生的文件,若是Makefile ,configure 便會把它檢查出來的結果帶入 Makefile.in 文件後產生合適的 Makefile。 實際上,這裡使用 Automake 時,還需要一些其他的巨集,這些額外的巨集我們用 aclocal來幫助產生。執行 aclocal會產生aclocal.m4 文件,如果無特別的用途,可以不需要修改它,用 aclocal 所產生的巨集會告訴 Automake如何動作。
有了 configure.in 及 aclocal.m4兩個文件以後,便可以執行 autoconf來產生 configure 文件了。
4.2 編輯Makefile.am 文件 接下來要編輯Makefile.am 文件,Automake 會根據 configure.in 中的巨集把Makefile.am 轉成 Makefile.in 文件。 Makefile.am 文件定義所要產生的目標:
AUTOMAKE_OPTIONS 設置 automake 的選項。Automake 主要是幫助開發 GNU 軟件的人員來維護軟件,所以在執行 automake 時,會檢查目錄下是否存在標準 GNU 軟件中應具備的文件,例如 'NEWS'、'AUTHOR'、'ChangeLog' 等文件。設置 foreign 時,automake 會改用一般軟件的標準來檢查。 bin_PROGRAMS 定義要產生的執行文件名。如果要產生多個執行文件,每個文件名用空白符隔開。 hello_SOURCES 定義 'hello' 這個執行程序所需要的原始文件。如果 'hello'這個程序是由多個原始文件所產生,必須把它所用到的所有原始文件都列出來,以空白符隔開。假設 'hello' 還需要 'hello.c'、'main.c'、'hello.h' 三個文件的話,則定義 hello_SOURCES= hello.c main.c hello.h 如果定義多個執行文件,則對每個執行程序都要定義相對的filename_SOURCES。
編輯好 Makefile.am 文件,就可以用 automake --add-missing來產生 Makefile.in。加上 --add-missing 選項來告訴 automake順便假如包裝一個軟件所必須的文件。Automake產生生出來的 Makefile.in 文件是完全符合 GNU Makefile 的慣例,只要執行 configure這個shell script 便可以產生合適的 Makefile 文件了。
4.3 使用 Makefile 利用 configure 所產生的 Makefile文件有幾個預先設定的目標可供使用,這裡只用幾個簡述如下:
make all 產生設定的目標,既次範例中的執行文件。只敲入make 也可以,此時會開始編譯源代碼,然後連接並產生執行文件。 make clean 清除之前所編譯的執行文件及目標文件(object file, *.o)。 make distclean 除了清除執行文件和目的文件以外,也把 configure 所產生的 Makefile 清除掉。 make install 將程序安裝到系統中,若源碼編譯成功,且執行結果正確,便可以把程序安裝到系統預先設定的執行文件存放路徑中,若用 bin_PROGRAMS 巨集的話,程序會被安裝到 /usr/local/bin下。 make dist 將程序和相關的文檔包裝為一個壓縮文檔以供發佈 (distribution) 。執行完在目錄下會產生一個以PACKAGE-VERSION.tar.gz 為名稱的文件。PACKAGE 和 VERSION 這兩個參數是根據 configure.in 文件中 AM_INIT_AUTOMAKE(PACKAGE, VERSION) 的定義。在此範例中會產生 'hello-1.0.tar.gz' 的文件。 make distcheck 和 make dist 類似,但是加入檢查包裝以後的壓縮文件是否正常,這個目標除了把程序和相關文檔包裝成 tar.gz 文件外,還會自動把這個壓縮文件解開,執行 configure,並執行 make all ,確認編譯無錯誤以後,戶顯示這個 tar.gz 文件已經準備好可以發佈了。這個檢查非常有用,檢查過關的套件,基本上可以給任何具備 GNU 開發環境的人去重新編譯成功。就 hello-1.tar.gz 這個範例而言,除了在Red Hat Linux 上,在 FreeBSD 2.2.x 也可以正確編譯。 要注意的是,利用 Autoconf 及 Automake 所產生出來的軟件套件是可以在沒有安裝 Autoconf 及 Automake 的環境使用的,因為 configure 是一個 shell script,它己被設計為可以在一般 Unix 的 sh 這個 shell 下執行。但是如果要修改 configure.in 及 Makefile.am 文件再產生新的 configure 及 Makefile.in 文件時就一定要有 Autoconf 及 Automake 了。

5. 相關資料
Autoconf 和 Automake 功能十分強大,可以從它們附帶的 info 穩當4中找到詳細的使用方法說明。你也可以從許多現有的GNU 軟件或 Open Source 軟件中找到相關的 configure.in 或 Makefile.am 文件,他們是學習 Autoconf 及 Automake 更多技巧的最佳範例。
這個簡介只用到了 Autoconf 及 Automake 的皮毛罷了,如果你有心加入 Open Source 軟件開發的行列,希望這篇文章可以幫助你對產生 Makefile 有個簡單的瞭解。其它有關開發 GNU 程式或 C 程序設計及 Makefile 的詳細運用及技巧,建議從 GNU Coding Standards (GNU 編碼規定) 讀起,裡面包含了 GNU Makefile 慣例,及開發 GNU 軟件的標準程序和慣例。這些 GNU 軟件的在線說明文件可以在 http://www.gnu.org/ 上找到。

6. 結束語
利用 Autoconf 及 Automake,產生一個 Makefile 似乎不再像以前那麼困難了,而使用 Autoconf 也使得我們在不同平台上或各家 Unix 之間發佈及便宜程序變的簡單,這對於在Unix 系統上程序開發員來說減輕了許多負擔。妥善運用這些 GNU 的工具軟件,可以幫助我們更容易的去開發程序,而且更容易維護源代碼。

[工具] Source Insight with Artistic Style

Written on 5:18 下午 by Yu Lai

Windows平台下也有好多人都喜歡用SourceInsight編輯C/C++程序,但是SourceInsight沒有提供對代碼格式化的功能,如果將Artistic Style集成到SourceInsight中,那就可以為它擴展出代碼格式化的功能了。

假定AStyle.exe的目錄是"C:\ArtisticStyle\"。下面簡要地介紹下Artistic Style集成到SourceInsight中的方法。
1. 打開你的SourceInsight, 選擇菜單"Options-->Custom Commands-->Add", 輸入Artistic Style(可以隨便輸入一個名字)。
2. Run中輸入: C:\ArtisticStyle\Astyle.exe --style=linux --indent=tab %f
3. Dir留空,將Iconic Window, Capture Output, Parse Links in OutPut, File,then Line 四項前打上勾。
4. 然後點對話框中右側的按鈕“Menu”, Menu--->Menu-->View-->, 右側Insert, OK.
5. 此時在SourceInsight中的View菜單下多了個Style的子菜單選項,可以用它來對單個C/C++文件進行格式化。

[技術] uClinux上Berkeley DB v4.5.20移植手記

Written on 4:01 下午 by Yu Lai

轉載自: http://blog.iyi.cn/hily/archives/2006/10/uclinuxberkeley_db_v4520.html

Berkeley DB 是一個很棒的開源的嵌入式數據庫,它主要運行在Unix/Linux上。現在它已成為Oracle的一部分,叫作Oracle Berkeley DB
下面主要介紹一下它在我最近玩的uClinux上的移植過程。

Hily Jiang
Email&Gtalk: hilyjiang at Gmail
Blog: http://hily.iyi.cn/

下載頁面:
http://dev.sleepycat.com/downloads/releasehistorybdb.html

(一)解壓

linux:/home/work/db # tar -zxf db-4.5.20.tar.gz
linux:/home/work/db # cd db-4.5.20/
linux:/home/work/db/db-4.5.20 # ls
. db_checkpoint db_upgrade libdb_java README
.. db_deadlock db_verify LICENSE rep
btree db_dump dist lock repmgr
build_unix db_dump185 docs log rpc_client
build_vxworks db_hotbackup env mod_db4 rpc_server
build_windows dbinc examples_c mp sequence
clib dbinc_auto examples_cxx mutex tcl
common db_load examples_java os test
crypto dbm fileops os_vxworks txn
cxx db_printlog hash os_windows xa
db db_recover hmac perl
db185 dbreg hsearch php_db4
db_archive db_stat java qam

docs目錄下有我們需要的文檔,包括快速入門、各種語言的API手冊等資料。

(二)配置和編譯
建立一個腳本以方便配置。由於unix/linux編譯時的工作路徑必須是build_unix,因此我們需要在build_unix目錄下創建腳本。
我創建了一個名為myconfig的腳本,內容如下:

1 #!/bin/sh
2
3 CC=arm-elf-gcc \
4 CFLAGS="-Os -D__uClinux__ -fno-builtin -I/home/uClinux-dist/linux-2.4.x/include -I/home/uClinux-dist/lib/uClibc/include -I/home/uClinux-dist/lib/uClibc/include/../ " \
5 LDFLAGS="-Wl,-elf2flt -Wl,-move-rodata -L/home/uClinux-dist/lib/uClibc/lib -L/home/uClinux-dist/lib/uClibc/lib/../ -lc " \
6 ../dist/configure \
7 --prefix=/home/work/db/Berkeley.DB \
8 --build=i686-linux \
9 --host=arm-elf-linux \
10 --disable-cryptography \
11 --disable-hash \
12 --disable-queue \
13 --disable-replication \
14 --disable-statistics \
15 --disable-verify \
16 --disable-compat185 \
17 --disable-cxx \
18 --disable-debug \
19 --disable-debug_rop \
20 --disable-debug_wop \
21 --disable-diagnostic \
22 --disable-dump185 \
23 --disable-java \
24 --disable-mingw \
25 --disable-o_direct \
26 --disable-posixmutexes \
27 --disable-pthread_api \
28 --disable-rpc \
29 --disable-smallbuild \
30 --disable-tcl \
31 --disable-test \
32 --disable-uimutexes \
33 --enable-umrw \
34 --disable-shared \
35 --enable-static \
36 --enable-fast-install \
37 --disable-libtool-lock \
38 --disable-largefile

關於configure配置參數的含義,可以運行"../dist/configure --help"查看幫助,這裡不再介紹。
要強調的一點是uClibc只能用靜態編譯,因此一定要選擇--disable-shared。
接著執行./myconfig運行配置並編譯安裝函數庫:
linux:/home/work/db/db-4.5.20/build_unix # ./myconfig >/dev/null
configure: WARNING: In the future, Autoconf will not detect cross-tools
whose name does not start with the host triplet. If you think this
configuration is useful to you, please write to autoconf@gnu.org.
cat: /etc/ld.so.conf.d/*.conf: No such file or directory
linux:/home/work/db/db-4.5.20/build_unix # make >/dev/null && make install >/dev/null
../dist/../hmac/sha1.c:96: warning: `R0' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:40: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:97: warning: `R1' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:42: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:98: warning: `R2' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:44: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:99: warning: `R3' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:46: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:100: warning: `R4' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:48: warning: this is the location of the previous definition
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
strip: /home/work/db/Berkeley.DB/bin/db_archive: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_checkpoint: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_deadlock: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_dump: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_hotbackup: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_load: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_printlog: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_recover: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_stat: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_upgrade: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_verify: 不可識別的文件格式

編譯過程中會出現一些warning,不用理它們。
安裝完後,會在指定的安裝目錄/home/work/db/Berkeley.DB下生成函數庫:
linux:/home/work/db/db-4.5.20/build_unix # cd /home/work/db/Berkeley.DB/
linux:/home/work/db/Berkeley.DB # ls
. .. bin docs include lib
linux:/home/work/db/Berkeley.DB # ll lib/
總用量 1962
drwxr-xr-x 2 root root 104 2006-10-08 12:57 .
drwxr-xr-x 6 root root 144 2006-10-08 12:57 ..
-rw-r--r-- 1 root root 1002266 2006-10-08 12:57 libdb-4.5.a
-rw-r--r-- 1 root root 1002266 2006-10-08 12:57 libdb.a


(三)數據庫操作測試
創建一個測試程序如下:
linux:/home/work/db/Berkeley.DB # cat -n testdb.c
1 #include
2 #include
3 #include
4
5 #define DESCRIPTION_SIZE 20
6 int main()
7 {
8 DB *dbp; /* DB structure handle */
9 u_int32_t flags; /* database open flags */
10 int ret; /* function return value */
11 char *description = "Grocery bill.";
12 char *description1[DESCRIPTION_SIZE + 1];
13 DBT key, data;
14 float money;
15
16 /* Initialize the structure. This
17 * database is not opened in an environment,
18 * so the environment pointer is NULL. */
19 ret = db_create(&dbp, NULL, 0);
20 if (ret != 0) {
21 /* Error handling goes here */
22 printf("Create fail!\n");
23 }
24
25 /* Database open flags */
26 flags = DB_CREATE; /* If the database does not exist,
27 * create it.*/
28
29 /* open the database */
30 ret = dbp->open(dbp, /* DB structure pointer */
31 NULL, /* Transaction pointer */
32 "/home/my_db.db", /* On-disk file that holds the database. */
33 NULL, /* Optional logical database name */
34 DB_BTREE, /* Database access method */
35 flags, /* Open flags */
36 0); /* File mode (using defaults) */
37 if (ret != 0) {
38 /* Error handling goes here */
39 printf("Created new database.\n");
40 }
41
42 money = 122.45;
43
44 /* Zero out the DBTs before using them. */
45 memset(&key, 0, sizeof(DBT));
46 memset(&data, 0, sizeof(DBT));
47
48 key.data = &money;
49 key.size = sizeof(float);
50
51 data.data = description;
52 data.size = strlen(description) +1;
53
54 ret = dbp->put(dbp, NULL, &key, &amp;amp;amp;data, DB_NOOVERWRITE);
55 if (ret == DB_KEYEXIST) {
56 dbp->err(dbp, ret,
57 "Put failed because key %f already exists", money);
58 }
59
60 memset(&data, 0, sizeof(DBT));
61
62 data.data = &description1;
63 data.ulen = DESCRIPTION_SIZE + 1;
64 data.flags = DB_DBT_USERMEM;
65 dbp->get(dbp, NULL, &key, &amp;amp;amp;data, 0);
66
67 printf("data: %s\n", (char *)data.data);
68
69 /* When we're done with the database, close it. */
70 if (dbp != NULL)
71 dbp->close(dbp, 0);
72
73 return 0;
74 }

這個程序會在目標板上/home/目錄下創建一個文件名為my_db.db的數據庫,接著增加一條記錄,然後從數據庫中讀取出新添加的這條記錄,最後關閉數據庫。
程序要燒寫到目標板上,需要進行交叉編譯:
linux:/home/work/db/Berkeley.DB # arm-elf-gcc -O3 -Wall -mapcs-32 -mtune=arm7tdmi -fno-builtin -msoft-float -Os -D__uClinux__ -D__ARM_CPU__ -I/home/work/uClinux-dist/lib/uClibc/include -I/home/uClinux-dist/linux-2.4.x/include -I/home/work/db/Berkeley.DB/include -D_DEBUG_ -c testdb.c -o testdb.o
linux:/home/work/db/Berkeley.DB # arm-elf-gcc testdb.o -nostartfiles -Wl, -elf2flt -L/home/uClinux-dist/lib/uClibc/lib -L/home/work/db/Berkeley.DB/lib /home/uClinux-dist/lib/uClibc/lib/crt0.o /home/uClinux-dist/lib/uClibc/lib/crti.o /home/uClinux-dist/lib/uClibc/lib/crtn.o -lc -ldb -o testdb
linux:/home/work/db/Berkeley.DB # ll
總用量 1217
drwxr-xr-x 6 root root 280 2006-10-08 13:22 .
drwxr-xr-x 4 root root 144 2006-10-08 12:50 ..
drwxr-xr-x 2 root root 376 2006-10-08 12:57 bin
drwxr-xr-x 14 root root 384 2006-10-08 12:57 docs
drwxr-xr-x 2 root root 96 2006-10-08 12:57 include
drwxr-xr-x 2 root root 104 2006-10-08 12:57 lib
-rwxr--r-- 1 root root 584476 2006-10-08 13:22 testdb
-rw-r--r-- 1 root root 2171 2006-10-08 13:22 testdb.c
-rw------- 1 root root 2163 2006-10-08 13:09 testdb.c~
-rwxr-xr-x 1 root root 673683 2006-10-08 13:22 testdb.gdb
-rw-r--r-- 1 root root 1540 2006-10-08 13:22 testdb.o

生成的可執行文件比較大,將近600KB。
燒寫到目標機上後運行,結果如下:
# /home/testdb
data: Grocery bill.

搞定~
總的來說,Berkeley DB還是比較易用的。就是生成的可執行文件還是太大些 :-(

[技術] 嵌入式系統入學測驗

Written on 10:19 上午 by Yu Lai

原文出處 A 'C' Test: The 0x10 Best Questions for Would-be Embedded Programmers: http://www.embedded.com/2000/0005/0005feat2.htm

以下是網路上找到中文版,作者不詳。

C語言測試是徵選嵌入式系統程式員過程中必須而且有效的方法。這些年我既參加也組織了許多這種測試,在這過程中我意識到這些測試能為面試者和被面試者提供許多有用訊息,此外,撇開面試的壓力不談,這種測試也是相當有趣的。

從被面試者的角度來講,你能了解許多關於出題者或監考者的情況。這個測試只是出題者為顯示其對ANSI標準細節的知識而不是技術技巧而設計嗎?這個愚蠢的問題嗎?如要你答出某個字符的ASCII值。這些問題著重考察你的系統調用(invoke)和記憶體分發策略方面的能力嗎?這反映出出題者也許花時間在微處理機上而不在嵌入式系統上。

如果上述任何問題的答案是“是”的話,那麼我知道我得認真考慮我是否應該去做這份工作。

從面試者的角度來講,一個測試也許能從多方面揭示應試者的素質。最基本的,你能了解應試者C語言的水準。不管怎麼樣,看一下這人如何回答他不會的問題也是滿有趣。應試者是以好的直覺做出明智的選擇,還是只是瞎蒙呢?當應試者在某個問題上卡住時是找藉口呢,還是表現出對問題的真正的好奇心,把這看成學習的機會呢?我發現這些訊息與他們的測試成績一樣有用。

有了這些想法,我決定出一些真正針對嵌入式系統的考題,希望這些令人頭痛的考題能給正在謀職的人一點幫住。這些問題都是我這些年實際碰到的。其中有些題很難,但它們應該都能給你一點啟發。

這個測試適用於不同水準的應試者,大多數初級水準的應試者的成績會很差,經驗豐富的程式員應該有很好的成績。為了讓你能自己決定某些問題的偏好,每個問題沒有分發分數,如果選擇這些考題為你所用,請自行按你的意思分發分數。


==============================================


預處理器 (Preprocessor)

1.用預處理指令#define 聲明一個常數,用以表示1年中有多少秒 (忽略閏年問題)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在這想看到幾件事情︰

a.#define 語法的基本知識 (例如︰不能以分號結束,括號的使用,等等)
b.懂得預處理器將為你計算常數表達式的值,因此,直接寫出你是如何計算一年中有多少秒會比直接計算出實際的值更清晰。
c.意識到這個表達式將使一個16位元的機器產生整數型溢位 - 因此要用到長整型符號L,告訴編譯器這個常數是的長整型數。
d.如果你在你的表達式中用到UL (表示無符號長整型) ,那麼你有了一個好的起點。記住,第一印象很重要。

2.寫一個“標準”巨集MIN ,這個巨集輸入兩個參數並返回較小的一個。

#define MIN(A, B) ((A) <= (B) ? (A) : (B))
這個測試是為下面的目的而設的︰

a.標識#define在巨集中應用的基本知識。這是很重要的,因為在行內(inline)運算子變為標準C的一部分之前,巨集是方便產生行內程式碼的唯一方法,對於嵌入式系統來說,為了能達到要求的性能,行內程式碼經常是必須的方法。
b.三元運算子的知識。這個運算子存在C語言中的原因是它使得編譯器能產生比if-then-else更優化的程式碼,了解這個用法是很重要的。
c.懂得在巨集中小心地把參數用括號括起來。我也用這個問題開始討論巨集的副作用,例如︰當你寫下面的程式碼時會發生什麼事?

least = MIN(*p++, b);

3.預處理器標識#error的目的是什麼?

如果你不知道答案,請看參考文獻1。這問題對區分一個正常的伙計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種問題的答案。當然如果你不是在找一個書呆子,那麼應試者最好希望自己不要知道答案。


==============================================


無窮迴圈 (Infinite loops)

4.嵌入式系統中經常要用到無窮迴圈,你怎麼樣用C編寫無窮迴圈呢?

這個問題用幾個解決方案。我首選的方案是︰

while(1)
{
...
}
一些程式員更喜歡如下方案︰
for(;;)
{
...
}
這個實作方式讓我為難,因為這個語法沒有確切表達到底怎麼回事。如果一個應試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的基本原理。如果他們的基本答案是︰“我被教著這樣做,但從沒有想到過為什麼。”這會給我留下一個壞印象。
第三個方案是用 goto︰
Loop:
...
goto Loop;
應試者如給出上面的方案,這說明或者他是一個組合語言程式員 (這也許是好事) 或者他是一個想進入新領域的BASIC/FORTRAN程式員。


==============================================


數據宣告 (Data declarations)

5.用變數a給出下面的定義:
a)一個整型數 (An integer)
b)一個指向整數的指標 (A pointer to an integer)
c)一個指向指標的指標,它指向的指標是指向一個整型數 (A pointer to a pointer to an integer)
d)一個有10個整數型的陣列 (An array of 10 integers)
e)一個有10個指標的陣列,該指標是指向一個整數型的 (An array of 10 pointers to integers)
f)一個指向有10個整數型陣列的指標 (A pointer to an array of 10 integers)
g)一個指向函數的指標,該函數有一個整數型參數並返回一個整數 (A pointer to a function that takes an integer as an argument and returns an integer)
h)一個有10個指標的陣列,該指標指向一個函數,該函數有一個整數型參數並返回一個整數 (An array of ten pointers to functions that take an integer argument and return an integer)


答案是︰

a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
人們經常聲稱這裡有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間裡,我確定我知道這個問題的答案。應試者如果不知道所有的答案(或至少大部分答案),那麼也就沒有為這次面試做準備,如果該面試者沒有為這次面試做準備,那麼他又能為什麼出準備呢?


==============================================


Static

6.關鍵字static的作用是什麼?

這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用︰
a.在函數本體內(in Function Block),一個被宣告為靜態的變數,在這一函數被呼叫過程中維持其值不變。
b.在一個Block(ie. {...} )內 (但在函數體外),一個被宣告為靜態的變數可以被Block內所有的函數存取, 但不能被Block外的其它函數存取。它是一個本地的全局變量。
c.在Block內,一個被聲明為靜態的函數,只可被這一Block內的其它函數呼叫。也就是這個函數被限制在宣告它的Block的本地範圍內使用。大多數應試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,因為他顯然不懂得本地化資料和程式碼範圍的好處和重要性。


==============================================


Const(常量)

7.關鍵字const有什麼含意?

我只要一聽到被面試者說︰“const意味著常數”,我就知道我正在和一個業餘者打交道。去年Dan Saks已經在他的文章裡完全概括了const的所有用法,因此ESP(譯者︰Embedded Systems Programming)的每一位讀者應該非常熟悉const能做什麼和不能做什麼。如果你從沒有讀到那篇文章,只要能說出const意味著“只讀”就可以了。儘管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細的答案,仔細讀一下Saks的文章吧。)如果應試者能正確回答這個問題,我將問他一個附加的問題︰下面的聲明都是什麼意思?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前兩個的作用是一樣,a是一個常數型整數。
第三個意味著a是一個指向常數型整數的指標(也就是,整型數是不可修改的,但指標可以)。
第四個意思a是一個指向整數的常數型指標(也就是說,指標指向的整數是可以修改的,但指標是不可修改的)。
最後一個意味著a是一個指向常數型整數的常數型指標(也就是說,指標指向的整數是不可修改的,同時指標也是不可修改的)。
如果應試者能正確回答這些問題,那麼他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關鍵字 const,也還是能很容易寫出功能正確的程式,那麼我為什麼還要如此看重關鍵字const呢 ?我有以下的幾個理由︰
a。關鍵字const的作用是給讀你程式碼的人傳達非常有用的訊息,實際上,宣告一個參數為常量是為了告訴了程式員這個參數的應用目的。如果你曾花很多時間清理其它人留下的垃圾(記憶體回收),你就會很快學會感謝這點多餘的訊息。(當然,懂得用const的程式員很少會留下的垃圾讓別人來清理的)
b。透過給優化器一些附加的訊息,使用關鍵字const也許能產生更緊湊的程式碼。
c。合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的程式碼修改。簡而言之,這樣可以減少bug的出現。


==============================================


Volatile(易變的)

8. 關鍵字volatile有什麼含意?並給出三個不同的例子。

一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在暫存器裡的備份。下面是volatile變量的幾個例子︰
a.並行設備的硬體暫存器 (如︰狀態暫存器)
b.一個中斷服務次程序中會訪問到的非自動變數(Non-automatic variables)
c.多執行緒應用中被幾個任務(task)共享的變數
回答不出這個問題的人是不會被僱佣的。(os:還好我不是讓這個人面試.....)我認為這是區分C程式員和嵌入式系統程式員的最基本的問題。搞嵌入式的家伙們經常同硬體、中斷、RTOS等等打交道,所有這些都要求用到volatile變量。不懂得volatile的內容將會帶來災難。
假設被面試者正確地回答了這是問題 (嗯,懷疑是否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。
Q 一個參數可以同時是const也是volatile嗎?解釋為什麼。
Q 一個指標可以是volatile 嗎?解釋為什麼。
Q 下面的函數有什麼錯誤︰

int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案︰
A 是的。舉的例子是"只讀的狀態暫存器"。它是volatile因為它可能被意想不到地改變。它是const因為程式不應該試圖去修改它。
A 是的。儘管這並不很常見。舉的例子是當一個執行中的次程序修該一個指向一個buffer 的指標時。
A 這段程式碼有點變態。這段程式碼的目的是用來返指標*ptr指向值的平方,但是,由於*ptr指向一個volatile型參數,編譯器將產生類似下面的程式碼︰
int square(volatile int *ptr)
{
int a, b;
a = *ptr;
b = *ptr;
return a * b;
}
由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段程式碼可能返回不是你所期望的平方值!正確的程式碼如下︰
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}


==============================================


位元操作 (Bit Manipulation)

9.嵌入式系統總是要用戶對變量或暫存器進行位操作。給定一個整型變量a,寫兩段程式碼,第一個設置a的bit 3,第二個清除a 的bit 3。在以上兩個操作中,要保持其它位不變。

對這個問題有三種基本的回應
- 不知道如何下手。該被面者從沒做過任何嵌入式系統的工作。
- 用bit fields。Bit fields是被扔到C語言死角的東西,它保證你的程式碼在不同編譯器之間是不可移植的,同時也保證了的你的程式碼是不可重新使用的。我最近不幸看到Infineon為其較複雜的通信晶片寫的驅動程式,它用到了bit fields因此完全對我無用,因為我的編譯器採用其它的模式來實現bit fields。從道德講︰永遠不要讓一個非嵌入式的家伙沾到實際硬體的邊。
- 用 #defines 和 bit masks 操作。這是一個有極高可移植性的方法, 是應該被用到的方法。
最佳的解決方案如下︰

#define BIT3 (0x1 << 3)
static int a;
void set_bit3(void) {
a = BIT3;
}
void clear_bit3(void) {
a &= ~BIT3;
}
一些人喜歡為設定和清除值而定義一個掩碼同時定義一些說明常數,這也是可以接受的。我希望看到幾個要點︰說明常數、= 和 &=~ 操作。


==============================================


存取固定的記憶體位置 (Accessing fixed memory locations)

10.嵌入式系統經常具有要求程式員去存取某特定的記憶體位置的特點。在某工程中,要求設定一個絕對位址為0x67a9的整數型變數的值為0xaa55。編譯器是一個純粹的ANSI編譯器。寫程式碼去完成這一任務。

這一問題測試你是否知道為了存取一絕對位址把一個整數型強製轉型 (typecast) 為一指標是合法的。這一問題的實作模式隨著個人風格不同而不同。 典型的類似程式碼如下︰

int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
一個較艱澀的方法是︰
*(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二種方案,但我建議你在面試時使用第一種方案。


==============================================


中斷 (Interrupts)

11.中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展-讓標準C支持中斷。具代表的事實是,產生了一個新的關鍵字 __interrupt。下面的程式碼就使用了__interrupt關鍵字去定義了一個中斷服務次程序(ISR),請評論一下這段程式碼的。

__interrupt double compute_area(double radius)
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}

這個函數有太多的錯誤了,以至讓人不知從何說起了︰
- ISR 不能返回一個值。如果你不懂這個,那麼你不會被雇用的。(os : 囧)
- ISR 不能傳遞參數。如果你沒有看到這一點,你被雇用的機會等同第一項。
- 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓多餘的暫存器入棧(PUSH入堆疊),有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。
- 與第三點一脈相承,printf()經常有重入和性能上的問題。 如果你丟掉了第三和第四點,我不會太為難你的。但如果你能得到後兩點,那麼你的被雇用前景越來越光明了。


==============================================


程式碼例子 (Code examples)

12.下面的程式碼輸出是什麼,為什麼?

void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}

這個問題測試你是否懂得C語言中的整數自動轉型原則,我發現極少有開發者懂得這些東西。不管如何,這unsigned int的答案是輸出是 "> 6"。原因是當表達式中存在有符號類型和無符號類型時所有的操作數都自動轉換為無符號類型(unsigned)。因此-20變成了一個非常大的正整數,所以該表達式計算出的結果大于6。這一點對於應當頻繁用到無符號數據類型的嵌入式系統來說是非常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。

13.評價下面的程式碼片斷︰

unsigned int zero = 0;
unsigned int compzero = 0xFFFF; /*1's complement of zero */

對于一個int型不是16位的處理器為說,上面的程式碼是不正確的。應編寫如下︰

unsigned int compzero = ~0;

這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經驗裡,好的嵌入式程式員非常準確地明白硬體的細節和它的限制,然而PC機程式往往把硬體作為一個無法避免的煩惱。
到了這個階段,應試者可能完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應試者不是很好,那麼這個測試就在這裡結束了。但如果顯然應試者做得不錯,那麼我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧…


==============================================


動態記憶體分發 (Dynamic memory allocation)

14.儘管不像非嵌入式計算機那麼常見,嵌入式系統還是有從堆積(heap)中動態分發內存的過程的。那麼嵌入式系統中,動態分發記憶體可能發生的問題是什麼?
這裡,我期望應試者能提到記憶體碎片,碎片收集的問題,變量的生命週期等等。這個主題已經在ESP雜誌中被廣泛地討論過了(主要是 P.J. Plauger,他的解釋遠遠超過我這裡能提到的任何解釋)。我拿出這麼一個小題目給應試者︰
下面的程式碼片段的輸出是什麼,為什麼?

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");

這是一個有趣的問題。最近在我的一個同事不經意把0值傳給了函數malloc,得到了一個合法的指標之後,我才想到這個問題。這就是上面的程式碼,該程式碼的輸出是"Got a valid pointer"。我用這個問題來開始討論,看看被面試者是否想到怎樣做才是正確的。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。


==============================================


Typedef

15.Typedef 在C語言中頻繁用以宣告一個已經存在的資料型態的同義字。也可以用預處理器做類似的事。例如,思考一下下面的例子︰

#define dPS struct s *
typedef struct s * tPS;

以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指標。哪種方法更好呢? (如果有的話)為什麼? 這是一個非常微妙的問題,任何人答對這個問題 (正當的原因) 是應當被恭喜的。
答案是︰typedef更好。思考下面的例子︰
dPS p1, p2;
tPS p3, p4;

第一個擴展為
struct s * p1, p2;

上面的程式碼定義p1為一個指向結構的指標,p2為一個實際的結構,這也許不是你想要的。
第二個例子正確地定義了p3 和p4 兩個指標。


==============================================


艱澀的語法

16.C語言允許一些令人震驚的結構,下面的結構是合法的嗎,如果是,它做些什麼?

int a = 5, b = 7, c;
c = a+++b;

這個問題將做為這個測驗的一個愉快的結尾。不管你相不相信,上面的例子是完全合法的。問題是編譯器如何處理它?水準不高的編譯作者實際上會爭論這個問題,根據最處理原則,編譯器應當能處理儘可能所有合法的用法。因此,上面的程式碼被處理成︰
c = a++ + b;

因此,這段程式碼執行後
a = 6, b = 7, c = 12

如果你知道答案,或猜出正確答案,做得好。如果你不知道答案,我也不把這個當作問題。我發現這個問題的最大好處是這是一個關於程式碼編寫風格,程式碼的可讀性,程式碼的可修改性的好的話題。


==============================================


好了,伙計們,你現下已經做完所有的測試了。這就是我出的C語言測試題,我懷著愉快的心情寫完它,希望你以同樣的心情讀完它。如果是認為這是一個好的測試,那麼盡量都用到你的謀職的過程中去吧。天知道也許過個一兩年,我就不做現下的工作,也需要找一個。
Nigel Jones 是一個顧問,現下住在Maryland。他很高興能收到讀者的來信,他的email位址是: NAJones@compuserve.com


==============================================

[知識]運動完之後餓了請照吃!勿食過量!

Written on 1:28 下午 by Yu Lai

引述自 Personal Fitness Training Theory & Practice

嗯,一般論都是運動後短時間內不應該吃東西,我來提一點不一樣的看法。

其實在運動之後,全身血液的分配會產生暫時性的變化,肌肉充血,而相對的流到脂肪細胞的血就少了。因為血量分配的改變,血中的養分對肌肉與脂肪細胞的分配也會改變,這樣的情況會在半小時內很快的恢復正常。

在有個動物實驗中,運動後的動物被分成兩組,一組在運動後立即給食物,另一組則在四小時候才餵食,十個星期後在體重上雖無顯著差異,但對動物身材卻有顯著影響。立即餵食的動物脂肪組織比延遲餵食的動物脂肪組織重量少24%,且肌肉組織重量增高。該研究的結論是運動後肌肉對養分的競爭力增高,對能源儲存的分配造成明顯的影響。

在人體實驗中也有相似的結果,運動後的肌肉組織對醣類的儲存能力,以及蛋白質的合成能力都暫時增加,但這樣的能力這會在兩小時內明顯減退。另外,肌肉對三酸甘油脂的吸收能力優勢可以維持六個小時。

站在這個立場,運動後的半小時內其實是攝取食物的黃金時段,身體在這時給了一個把脂肪組織打入冷宮的時間,不趁此時更待何時?此外,運動中身體裡的肝醣被大量消耗,因此身體會處在低血醣的狀態,不適時補充一些營養的話,等於為身體準備了一個適合分解肌肉的環境,還維持好幾個小時...你運動是練心酸的嗎?Orz

運動中是消耗,運動完當然要補充。惟補充的應該是一些好吸收消化的東西。另外,絕不可因為『太餓』而過量,這應該很多先賢提過了,所謂運動後馬上吃東西會胖都是因為吃太多...

[技術] 如何避免Linux zombie process的產生

Written on 12:44 下午 by Yu Lai

最近在幫同事debug所遇到的zombie process的問題,Survey了一些心得,就po上來囉。

自Advanced Programming in the Unix Environment這本書:
The process that has terminated, but whose parent hasn't yet waited for it.

簡單的說,所謂zombie process(殭屍行程)的成因,在於某子行程已結束,但其父行程(parent process)並未取得該行程之結束狀態,則該行程就會變成zombie process,直到其父行程呼叫wait()取得該行程結束狀態。

Zombie process會造成kernel內部有多餘的一筆process資料儲存,佔了一筆資料量(process id, termination status, the amount of CPU time taken by the process等等資訊),但其卻已經結束了,若是系統load很高,造成的影響就更大了,譬如process數量到達上限,又發生有zombie process,可能就會產生無法執行程式的問題。因此我們必須避免zombie process的產生。

解決的方法之一就是利用Linux會在一個process的父行程結束時,自動把該行程的父行程變成init,也就是讓該process由init接管。因此,假設我們的父行程必須作自己的事情,並不想wait子行程結束而block時,我們可以利用兩次fork,讓第一次fork的process馬上結束,則第二次fork的grandchild將會自動變成init的子行程,這樣一來,最原先的行程將不必在作wait的動作,而該行程從此也跟第二次fork出來的行程毫無親屬關係了。兩者各作各的,誰先結束並不會影響對方。這就是fork兩次的用意:


if pid = fork()
//parent waits for first child to exit
waitpid(pid);
parent does things here...
else
//child
if fork()
//child
exit;
else
//granchild
grandchild does things here...

[工具] E-Mail圖示產生器

Written on 11:07 上午 by Yu Lai

就是我右邊E-mail那個圖示的產生器囉,
在寫了前三篇後才發現忘了把這個Tool寫個Note啦。

http://services.nexodyne.com/email/

[工具] 筆記本產生器

Written on 11:02 上午 by Yu Lai

PocketMod 網站提供了一項非常有趣且特殊的服務,
您可以在線上設計自己的口袋型記事本,包含封面、
封底一共八頁,僅需使用一張 A4 的紙就可以完成。

http://www.pocketmod.com/app/index.html

[工具] 條紋背景產生器

Written on 11:00 上午 by Yu Lai

Stripe Generator 為一款很有趣的線上工具,
它能夠讓你在線上產生條紋樣式的背景。
可以設定的選項非常多,包含寬度、顏色、樣式、背景顏色、條紋方向等等。

http://www.stripegenerator.com/

[工具] 域名圖示產生器

Written on 10:55 上午 by Yu Lai

就像我blogger右邊Link那邊E-mail的圖示一樣,
這個可以產生Domain Name的圖示。

http://phorum.com.tw/Generator.aspx

[醒思] 你知道嗎?

Written on 10:20 上午 by Yu Lai

原文出處:http://blogs.myoops.org/lucifer.php/2007/04/04/p54


作者:朱學恆


你知道嗎?






請先看完這段影片吧!


2006年,位在科羅拉多州的一所默默無聞的公立高中Arapahoe剛結束暑假;新學期開始的時候,校長請該校科技中心的負責人Karl Fisch為老師們解說一下目前教育界的技術發展和趨勢。正好Arapahoe高中獲得了一筆基金會的贊助,幫學校換了兩百多台的電腦,於是Karl就決定以讓老師瞭解未來趨勢的角度來製作一套投影片,希望他們可以知道自己面對的是什麼樣的挑戰,並且進而能夠讓高中生們面對這樣的改變。Karl從書籍、網路、政府資料中整理出了一些數據,並且用淺顯易懂的比喻加入投影片中。(這些數據來自於「世界是平的」作者、教育界的知名人士、美國前教育部長、美國勞工部、麻省理工學院等等……)


在做好影片,配上音樂之後,他把這個影片取名為「Did you know?」;在校內老師面前第一次播放的時候,他還覺得忐忑不安,但幸好底下老師的反應很不錯,讓他覺得這影片似乎達成了他想要傳達的效果。


不過,他錯了。這達成的效果比他想像的還要大。


過了幾天之後,他把這段投影片放到自己的Blog上面供大家觀看。


立即有來自各地的使用者對這投影片感興趣,要求授權讓他們使用在不同的地方。熱心的使用者將檔案改編,重整,上傳到Youtube上和提供各種不同的格式。其他人則是轉寄、上課時播放給自己的學生看,研討會時拿來作引言……


到了現在,這個影片光是在Youtube上就有超過十種版本,將近兩千人把它加為最愛,三百多則對這個內容感到震撼的留言,全部瀏覽人次超過五十萬人以上。(這還不包括在網路以外的地方觀看的人數)


而我看到這影片的時候是在美國休士頓的Rice大學,現場聚集了全美知名學府的研究者,還有許多來自世界各地的學術界人士;掌握上億美金基金會教育經費的計畫負責人在會議一開始的時候一句話也不說的直接開始播放這個影片。


我們必須教導現在的學生,畢業後投入目前還不存在的工作...

使用根本還沒發明的科技...

解決我們從未想像過的問題。


影片播放完之後,現場一片寂靜,而她繼續接道:「轉變,正在發生。各位,我們所推廣的開放教育就是在為了這個趨勢作準備……」


當天,我就把這個影片重新翻譯成中文,請台灣的義工進行轉檔和影片製作,傳上了Youtube。

http://www.youtube.com/watch?v=xj9Wt9G--JY


(來看看吧!)


這個影片之所以讓人感到震撼,並不是因為它使用了華麗的特效或擁有超級大明星的陣容。它的震撼之處在於其中所引述的都是事實。它讓現在是世界第一強國的美國中教育界的佼佼者感到震撼,更讓環境、資源遠遜於美國的我感到震撼,我更覺得「我們」(父母、師長、教育家、政策決定者、立法行政官員)都應該一樣感到震撼。


美國前教育部長Richard Riley認為...

2010年最迫切需要的十種工作,在2004年時根本不存在。


因為這世界演變的快速已經遠超過我們的想像,我們本來應該要引導、協助新的世代面對這個劇烈變動的世局。但我們武器配備落後、訓練薄弱,更有各種各樣的強大的敵人虎視眈眈……我們和這些新的世代真的做好準備了嗎?


任天堂光是在2002年就投資一億四千萬美金進行研發。

美國聯邦政府花在教育研發上的經費還不到一半。


確實,驚天動地,以十倍速爆炸成長的事件正在發生。傳統的學習與教育系統都面對了最嚴苛的挑戰,對企業的忠誠度降低、對科技的依賴度增加,科技的進步又讓不會使用科技的人更加弱勢和絕望……


根據估計,《紐約時報》一週所包含的資訊量...

比十八世紀一個人一生可能接觸到的資訊量還要多。


這是一段真實的故事:紐約時報在2007年4月2號的科技版用不小的篇幅報導了我們進行的開放式課程翻譯計畫。而這個記者是在Youtube上面看到台灣一家虛擬媒體製作公司對我們訪談的影片,因此而對我產生興趣,這才找上我的。


隨即,新聞登出第二天我收到一封請求我幫忙的信件,寄信者是在看到報導之後在網路上找到我的Email而寄信過來請我幫忙翻譯麻省理工的一個網路廣告相關計畫。


這個人叫做Andreas Ramos,他是矽谷一家新創公司Position2的執行長。這家公司的工作是網路廣告代理搜尋最佳化。(聽起來很繞口對吧!)他的工作是把客戶所購買的搜尋引擎廣告效果最佳化。Google贊助了麻省理工價值四十五萬美金的網路廣告,但關鍵字和必須由他們自行管理。所以,麻省理工找上了Andreas Ramos,而他額外替麻省理工編寫了48,548個額外的關鍵字,並且透過演算法和網路行銷的技巧進行全球化的推廣。


結果呢?這些廣告每天的點閱次數從52次增加到17,650次,每次點閱所花費的廣告費用降為十分之一……


這個故事完全是真實的,但不過三到四年前,這裡面的技術、工作和機會,幾乎是完全不存在的。


所以,如果你的孩子不想當醫生、不想當太空人,而告訴你他們想要當網路廣告搜尋最佳化工程師的時候,你該怎麼辦?


對於這樣劇烈的跳躍和變化,我認為,知識的自由分享和創意是最重要的解決方案。(也因此我才要投入自己的時間和金錢進行開放式課程的推廣與分享)


唯有知識可以自由分享,人類才能夠創造出更多的知識,解決更多的問題,作出更正確的選擇。想想看,如果這個社會上的階級區分成「有錢」接受教育和「沒錢」接受教育這兩種;這還有什麼平等可言?


唯有跳躍性思考的創意才能夠迎接十倍速變化的世界。孩子們擁有結合包容和遠見的創意,才能看穿下一個世代的循環。線性按部就班的思考模式已經趕不上整個世界局勢的變化,唯有跳躍、不受傳統規則束縛、激烈變革,直接得出結論的創意訓練才能夠及時提出解決方案。


在中國,智商排名前四分之一的人...

比北美洲的總人口還要多。

對全世界老師的意義:他們的優秀學生,比我們所有的學生還要多。


這些驚天動地的轉變正在發生,你已經知道了,然後呢?


(本文歡迎隨處轉貼、轉寄,僅需註明出處和作者即可)

[技術] JavaScript Library

Written on 5:48 下午 by Yu Lai

http://script.aculo.us/
這裡有許多現成的JavaScript Library,有需要的話還蠻實用的啦。

[閒聊] 工程師名言錄

Written on 11:05 上午 by Yu Lai

這是在 Hemidemi 看到的,原文在: Top 20 replies by Programmers to Testers when their programs don't work。翻譯如下:

第 20 名:這很奇怪喔。

第 19 名:以前從來不會這樣啊!

第 18 名:昨天明明會動的啊!

第 17 名:怎麼可能~

第 16 名:這一定是機器的問題。

第 15 名:你到底是打了什麼才讓程式當掉的?

第 14 名:一定是你的資料有問題。

第 13 名:我已經好幾個禮拜沒碰那一段程式了。

第 12 名:你一定是用到舊版了。

第 11 名:一定是巧合!為什麼這種壞運氣只讓你碰上。

第 10 名:我不可能什麼功能都測試到吧,有 bug 是正常的!

第 9 名:這個不可能是那個的原始碼!

第 8 名:這程式應該是會動的,只是我寫好後還沒做測試。

第 7 名:可惡!一定有人改了我的程式。

第 6 名:你有檢查過你的電腦有沒有病毒嗎?

第 5 名:儘管這功能還不能動啦,你覺得他如何?

第 4 名:在你的系統不能用那一個版本的程式啦!

第 3 名:你幹嘛要那樣操作,都是你的問題。

第 2 名:程式發生問題時你在哪裡?

第 1 名:在我的機器明明就可以動啊!

所以說嘛!可以做人,幹嘛來寫程式呢?

[閒聊] 好久沒更新了

Written on 7:16 下午 by Yu Lai

最近工作較忙,久久未更新內容。
今天剛好拿到公司發的新NB,是T60唷(爽~!)。
再來應該會比較常上來更新啦,希望希望(遠目)。

[技術] FreeBSD native wireless support

Written on 3:42 下午 by Yu Lai

今天Intel正式授權FreeBSD可以使用旗下Wireless Chipset的Firmware.
包含: Intel 2100, 2200BG, 2225BG, 2915ABG, and the 3945ABG devices

這是一大福音阿, 尤其嘉惠要在FreeBSD上面做無線網路研究的人,
以前notebook上面跑FreeBSD都得要望centrino興嘆 (不然就是得用
其他方式啟動, 而且不太穩定) 現在有了官方的直接support, 實在太棒了 :D

有網頁有真相: http://www.prweb.com/releases/2007/03/prweb509818.htm

[閒聊] 中文的數字單位

Written on 10:43 上午 by Yu Lai

在去年有一篇英文的數字單位,今天在整理時順便找到了中文的數字單位,以下就是參考wikipedia的內容所整理出來的。

十進位漢字對照表
100
101
102
103
104
105十萬
106百萬
107千萬
108
109十億(吉)
1010百億
1011千億
1012兆(萬億)
1013十兆
1014百兆
1015千兆(拍)
1016
1017十京
1018百京(艾)
1019千京
1020
1021十垓(澤)
1022百垓
1023千垓
1024秭(堯)
1025十秭
1026百秭
1027千秭
1028
1029十穰
1030百穰
1031千穰
1032
1033十溝
1034百溝
1035千溝
1036
1037十澗
1038百澗
1039千澗
1040
1041十正
1042百正
1043千正
1044
1045十載
1046百載
1047千載
1048
1049十極
1050百極
1051千極
1052恆河沙
1053十恆河沙
1054百恆河沙
1055千恆河沙
1056阿僧祇
1057十阿僧祇
1058百阿僧祇
1059千阿僧祇
1060那由他
1061十那由他
1062百那由他
1063千那由他
1064不可思議
1065十不可思議
1066百不可思議
1067千不可思議
1068無量大數
1069十無量大數
1070百無量大數
1071千無量大數
1072Espana
1073十Espana
1074百Espana
1075千Espana
1076
1077
1078
1079
1080
1081
............
10100Googol
十退制漢字對照表
100
10-1
10-2
10-3
10-4
10-5
10-6
10-7
10-8
10-9塵(納)
10-10
10-11
10-12漠(皮)
10-13模糊
10-14逡巡
10-15須臾(飛)
10-16瞬息
10-17彈指
10-18剎那(阿)
10-19六德
10-20空虛
10-21清靜(仄)
10-22阿賴耶
10-23阿摩羅
10-24涅槃寂靜(攸)
10-25
10-26
10-27
10-28
10-29
10-30
10-31
10-32
10-33
10-34
10-35
10-36
10-37
10-38
10-39
10-40
10-41
10-42
10-43
10-44
10-45
10-46
10-47
10-48
10-49
10-50
10-51
10-52
10-53
10-54
10-55
10-56
10-57
10-58
10-59
10-60
10-61
10-62
10-63
10-64
10-65
10-66
10-67
10-68
10-69
10-70
10-71
10-72
10-73
10-74
10-75
10-76
10-77
10-78
10-79
10-80
10-81
10-82
10-83

註:

  • 亦作
  • 亦作
  • 是正確寫法,而不是。
  • 微細的,是自天竺的佛經上的數字。而這些「佛經數字」已成為「古代用法」了。

[技術] Round Robin DNS

Written on 9:15 上午 by Yu Lai

最近本來很低調的FTP因為流量越來越大導致不怎麼低調了。
所以只好去Survey了幾個低調的做法。考慮了許多effort因素
最後採用的就是使用Round Robin DNS技術將流量打散在許多
IP上,也是目前蠻常見的低成本負載平衡。至於成效目前還未知
,但我想應該是還不錯的啦。
以下是在Bind上設定Round Robin DNS的一些notes:


;Dynamic FTP by Round Robin DNS
dynftp 0 IN A 192.168.123.101
dynftp 0 IN A 192.168.123.102
dynftp 0 IN A 192.168.123.103
...

其中,dynftp的A-Records即為Round Robin設置,
0為TTL(Time to live),單位為秒。若DNS Server因此
Loading過重,可酌量提高TTL數值。

[閒聊] 沒事改改template

Written on 11:26 上午 by Yu Lai

今天臨時想到把template改成類似Mac的金屬面板 XD

[技術] Net-SNMP的mib2c

Written on 5:37 下午 by Yu Lai

今天在搞net-snmp所提供的mib2c tool,
用起來說實在的實在有點難用。
大概是我對它還不太熟的關係吧。
以下是今天弄的一些心得:
(因為開發需求,所以設定prefix為/opt/net-snmp。)

1.
首先,在安裝net-snmp時,需在configure時加入以下參數


$ ./configure --prefix=/opt/net-snmp/ --enable-embedded-perl --enable-shared --with-perl-modules
$ make
$ make test
# make install (should be root)


2.
在確定PATH設定好後,執行mib2c時,
通常會遇到訊息說沒有安裝Perl SNMP Module,
這是因為我們在configure時有設了prefix的關係,
導致perl找不到share library。
解決的方法是將net-snmp的lib目錄設定給ldconfig知道即可。

# echo /opt/net-snmp/lib > /etc/ld.so.conf.d/net-snmp.conf
# ldconfig -v


3.
最後就是mib2c操作方法,
據mib2c的manpage裡說明,
mib2c的功用是產生template code給agent來擴充功能使用。
Usage為
mib2c [-h] -c CONFIGFILE [-I PATH] [-f OUTNAME] [-i][q][-S VAR=VAL] MIBNODE

而mib2c的使用方法是以mib node為parameter來開始生成template code。
所以必須先將mib file放置在$HOME/.mibs/或/opt/net-snmp/share/snmp/mibs,
然後在環境變數中設定MIBS,
export MIBS=+NET-SNMP-TUTORIAL-MIB

接著麻煩的在這裡,
你必須依著你的的mib node來選定所產生的template code的CONFIGFILE。
下面是CONFIGFILE所支援的類型:
如果你要寫些scalars所需的code,請用:
mib2c -c mib2c.scalar.conf MIBNODE

如果你要用int scalars,請用:
mib2c -c mib2c.int_watch.conf MIBNODE


一般來講Table是建議採用MDF:
mib2c -c mib2c.mfd.conf MIBNODE

如果Table Data是在外部時:
mib2c -c mib2c.iterate.conf MIBNODE

如果Table Data是在agent裡:
mib2c -c mib2c.create-dataset.conf MIBNODE

承上,如果還需排序時:
mib2c -c mib2c.array-user.conf MIBNODE


如果是要產生Table上任一Column的header時:
mib2c -c mib2c.column_defines.conf MIBNODE

如果是要產生Column上任一Enum的header時:
mib2c -c mib2c.column_enums.conf MIBNODE


4.
結論,大概就是麻煩吧。
Net-SNMP不像其他商用的SNMP套件一樣提供簡易的開發擴充功能的環境,
像在EMANATE中打一個make就ok了,連型態都自動判斷。
不過也許是我對mib2c還不太熟的關係吧,Orz。

[技術] 製作 ARM9 的 Bootstrap Root Filesystem

Written on 7:40 下午 by Yu Lai

本文出處: www.jollen.org
已取得原作者授權使用

《Jollen的Root Filesystem建置技術系列》

製作ARM9的Bootstrap Root Filesystem

作者/陳俊宏

http://www.jollen.org

更新日期:2007/1/23

在「完整註明出處」的前提下(註明方式說明),您能立即擁有轉貼與引用的授權,且毋需知會作者。

目的

製作 bootstrap root filesystem(base root filesystem)以提供一個最簡單、陽春且可開機的環境;製作完成的系統可開機到shell模式,並可使用 busybox 提供的指令。

準備工作

首先,您必須準備一台 host 開發環境,並安裝好 cross toolchain;接著,由於本文是做實機測試,因此,如果您沒有 ARM9 開發板,可以考慮使用 Qemu 來做模擬測試。

以下的操作示範,只節錄重點指令片段,您可能必須根據自己的整體實作流程,來微調指令的順序,或是參數等。

Step 1:建立工作目錄

建立一個專用的工作目錄,命名為 arm9.so-busybox/:

# mkdir arm9.so-busybox/

# cd arm9.so-busybox/

接著在 arm9.so-busybox/ 目錄下建立 4 個子目錄:

# mkdir src/ install/ mnt/ pub/ build/

實際進行 root filesystem 實作時,我們應該養成將檔案分類擺放的好習慣。以本專案為例,build/ 目錄用來編譯程式,src/ 目錄用來存放原始程式碼,install/ 目錄則用來擺放我們最後的 root filesystem。

Step 2:建立目錄架構

根據 FHS 的目錄架構標準,在 root filesystem 目錄下(install/)建立目錄階層架構:

# cd install/

# mkdir bin/ dev/ etc/ mnt/ proc/ sbin/ usr/

另外還有二個必要的目錄:/var 與 /tmp,由於這二個目錄都需要具備寫入權限,所以在這裡我們是以 ramdisk 的做法來 mount 這二個目錄。

Step 3:建立裝置檔

在 root filesystem 的 dev/ 目錄下建立必要的裝置檔:

crw------- 1 root root 5, 1 1月 1 1970 console

crw------- 1 root root 29, 0 1月 1 1970 fb0

crw------- 1 root root 1, 3 1月 1 1970 null

brw------- 1 root root 1, 0 1月 1 1970 ram0

crw------- 1 root root 5, 0 1月 1 1970 tty

crw------- 1 root root 4, 0 1月 1 1970 tty0

此階段使用 mknod 指令來完成。請先切換到 root filesystem 的 dev/ 目錄下,接著執行以下指令:

# mknod console c 5 1

# mknod fb0 c 29 0

# mknod null c 1 3

# mknod ram0 1 0

# mknod tty c 5 0

# mknod tty0 c 4 0

對於需要產生大量 device file 的場合來說,可以改用 genext2fs 的 ‘-D’ 參數來製作。詳見 Jollen’s Blog:[使用 genext2fs 的 '-D'(device file table)來建立 root filesystem]。

Step 4:加入Busybox

編譯並安裝 Busybox(動態程式庫方式)。將取得的Busybox原始碼解壓縮至 project 目錄裡的 src/ 子目錄下,以下是幾個注意事項:

本教學文件使用 Busybox 1.3.1

Busybox 1.3.0 開始,使用 Linux Kernel 的 Makefile(因為開始支援 CONFIG_DESKTOP)。Cross compile 時,需要修改 Makefile 如下:

ARCH ?= arm

CROSS_COMPILE ?= /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-

CROSS_COMPILE 的設定是 cross toolchain 的「PREFIX」,視您的 toolchain 而定。您可由 http://www.jollen.org/kit/ 下載本文所使用的 GCC 3.4.1 ARM9 toolchain,以使用與本文完全相同的修改。

Busybox 整合了常用的指令與工具,我們可以設定 Busybox,以勾選我們需要的功能選項。進入 Busybox 的設定選單:

# make menuconfig

請注意,init 與 shell 是必選的項目,請檢查是否有勾選這二個功能。同時,也別忘了設定 Busybox 的安裝路徑,將安裝路徑指到我們 root filesystem 目錄下。

接著直接進行編譯(cross compile):

# make

編譯完成後,將 Busybox 安裝至我們的 root filesystem 目錄(即 Step 2 的 install/ 目錄):

# make install

此時,您應該可以在 root filesystem 目錄下看到 Busybox 所安裝的檔案。

Step 5:加入動態程式庫

編譯完成的 Busybox 已經是給 ARM9 執行的格式了,但我們的編譯設定是將Busybox 編譯成 shared library 架構,因此 Busybox 執行時需要以下的檔案:

˙ libc.so.6:C library標準程式庫。

˙ ld-linux.so.2:Native dynamic loader。

請由 toolchain 將以上二個檔案複製至 root filesystem 的 lib/ 目錄下:

# cd ../../install (切換至root filesystem根目錄)

# cp /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/ld-linux.so.2 lib/ (複制native dynamic loader。以上命令請勿斷行)

# cp /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/libc.so.6 lib/ (複製C library。以上命令請勿斷行)

Busybox 會因版本與功能選項設定的差異,而需要更多的程式庫。請使用 cross toolchain 的 objdump 指令來檢查 Busybox 的程式庫相依問題(無法使用 ldd 指令),並將所需的程式庫由 toolchain 複製到 root filesystem 的 lib/ 目錄下。

Step 6:加入系統檔案

加入2個重要的系統檔案於 etc/ 目錄下:

˙ fstab:mount table。

˙ inittab:系統初始表(init table)。

etc/fstab內容如下:

/dev/ram0 / ext2 defaults 1 1

none /proc proc defaults 0 0

/dev/ram1 /tmp ramfs defaults 0 0

/dev/ram2 /var ramfs defaults 0 0

fstab 第一行設定,目的在將 /dev/ram0 重新附掛成 ‘/’(root),此動作用意在於重新指定 ‘/’ 的檔案系統為 ext2。最後二行的目的是為了以 ramfs 來 mount 重要的二個目錄:/var 與 /tmp;如此一來,就算開機沒做 remount root(詳見後文說明),也能對 /tmp 與 /var 目錄做寫入的動作

etc/inittab內容如下:

:0:sysinit:/etc/rc.d/rc.init

:0:respawn:/bin/sh

根據這個 inittab 設定,當系統開機後便會進入 run level 0,在 run level 0 模式下,init process會執行2個動作:(1) 執行 /etc/rc.d/rc.init,此即「init script」;(2) 執行 /bin/sh,即進入 shell 模式。

在此我們並沒有參照 LSB 的標準來設定 run level,而且也沒有使用 getty 來讓使用者登入(多使用者模式)。

Step 7:編寫 Initial Script

根據 inittab 的設定,我們 root filesystem 的 init script 位於 /etc/rc.d/rc.init。以下提供一個供 Embedded Linux 使用的 init script 範本:

#!/bin/sh

# automount (/etc/fstab)

mount -a

# remount root

mount -o remount rw /

#

mkdir /var/lock

mkdir /var/lock/subsys

mkdir /var/run



# start other applications (Running application automatically during

# booting up.

# eg. /bin/thttpd –p 80 –d /var/www

當我們執行「mount –a」後,mount 便會去讀取前一步驟所設定的 fstab,並根據此表格的內容來做 mount 的動作。另外,這裡有一個 remount 的動作:

# mount -o remount rw /

此動作的目的是將 root(’/’)重新 mount 成可讀寫,此動作是選擇性的,若省略不做,請務必保持 /var 與 /tmp 目錄是能寫入的(建議以 ramdisk 方式實作為佳)。

若 root filesystem 未包含 inittab 設定檔,則 Busybox 會使用以下的內建設定:

::sysinit:/etc/init.d/rcS

::askfirst:/bin/sh

::ctrlaltdel:/sbin/reboot

::shutdown:/sbin/swapoff -a

::shutdown:/bin/umount -a -r

::restart:/sbin/init

不過,還是建議編寫自己的 inittab 設定檔。

Step 8:製作 Root Filesystem 映像檔(Image File)

截至目前為止,我們的檔案系統已經擁有基本的系統指令與工具。接下來,我們即可將建置完成的 root filesystem 製作成 ext2 格式的映像檔。

以下提供二種 ext2fs image file 的製作方式:(1) 土方法;(2) 使用 genext2fs 工具。

先說明傳統的土方法。首先,先利用dd指令做出一個空白的檔案,大小為 4M(bytes):

# dd if=/dev/zero of=ext2fs bs=1k count=4096

我們將檔案命名為 ext2fs,接著再將 ext2fs 製作成 ext2 格式的檔案系統:

# mkfs.ext2 ext2fs

mke2fs 1.32(09-Nov-2002)

ext2fs is not a block special device.

Proceed anyway?(y,n)y

選擇y後出現以下畫面:

Filesystem label=

OS type: Linux

Block size=1024(log=0)

Fragment size=1024(log=0)

128 inodes, 1024 blocks

51 blocks(4.98%)reserved for the super user

First data block=1

1 block group

8192 blocks per group, 8192 fragments per group

128 inodes per group



Writing inode tables: done

Writing superblocks and filesystem accounting information: done



This filesystem will be automatically checked every 26 mounts or

180 days, whichever comes first. Use tune2fs -c or -i to override.

到這裡我們已經做好一個檔案格式為 ext2 的空白映像檔,再來只要將先前做好的 root filesystem 全部複製到 ext2fs 映像檔「裡面」即可。

先將 ext2fs 附掛至任一空目錄,例如 mnt/:

# mkdir mnt/

# mount -t ext2 -o loop ext2fs mnt/ (指定檔案系統為 ext2)

複製檔案系統時,我們不使用 cp 指令,而是利用 tar 來完成:

# cd install/

# tar cz * > ../install.tar.gz (將檔案系統做成tarball,同時也備份 root filesystem。)

# cd ..

# cd mnt/

# tar zxvf ../install.tar.gz (再將tarball解至映像檔)

接著將映像檔 umount 並壓縮即可:

# cd ..

# umount mnt/

# gzip -9c ext2fs > pub/ext2fs.gz

最後得到的 ext2fs.gz 即是完成品。請注意,若不使用 tar 來說,也應該使用 cpio 來複製檔案,避免使用 cp 指令。

使用 genext2fs

genext2fs 是一個 ext2 filesystem image file 的製作工具,可以讓我們很方便地將 root filesystem 製作成 image 檔。請由 genext2fs 的官方網站下載原始碼套件:

http://genext2fs.sourceforge.net/

編譯後可以取得 genext2fs 檔案,以下是將 install/ 目錄製作成 ext2fs image 檔的指令:

# genext2fs -b 8192 -i 1024 -d install/ ext2fs

執行後,會得到檔名為 ext2fs 的 image 檔,大小為 8 MB(透過 ‘-b’ 參數指定 image file 大小);接著同樣再用 gzip 將 ext2fs 檔壓縮即可。

Step 9:在 Target 端做測試

本步驟以 Jollen-Kit! 為例,Jollen-Kit! 是由 www.jollen.org 所推出的 ARM9 training board,詳細介紹請參考 [http://www.jollen.org/kit/]。請注意,本階段的操作,視 target device 的不同而不同,因此以下示範只適用於 Jollen-Kit! 或是其他的 SMDK2410 平臺。

步驟 8 所得到的 ext2.gz 必須再包裝成 U-Boot 的格式,才能透過 U-Boot 載入到 RAM,以成為 kernel 的 initial ramdisk(initrd):

# mkimage -A arm -O linux -T ramdisk -C none -a 0x30800000 -e 0x30800000 -n ramdisk -d ext2fs.gz urootfs.img

執行後可得到 urootfs.img 檔案,在測試階段為了方便起見,我們可以直接將 urootfs.img 載到 RAM 做測試;U-Boot 指令如下:

jollen.org # tftpboot 32000000 urootfs.img; tftpboot 30F00000 uimage.img; bootm 30F00000 32000000

urootfs.img 是我們製作的 root filesystem,uimage.img 則是給 Jollen-Kit! 使用的 Linux kernel(pre-built)。

[技術] 學習使用SkyEye仿真

Written on 9:08 上午 by Yu Lai

[轉自Experiences of Code (http://blog.csdn.net/zblue78)]

學習使用SkyEye仿真

--------------------------------------------------------------------------------


SkyEye是一個可以運行嵌入式操作系統的硬件仿真工具,這樣就可以在沒有硬件條件下來進行嵌入式系統的開發。

以下操作均在Fedora Core 1.0里通過。

Skyeye項目資源列表
http://gro.clinux.org/projects/skyeye/


文檔摘要:
1、什麼是SkyEye?
2、SkyEye可以做什麼事情?
3、安裝SkyEye
4、安裝arm-elf交叉編譯器
5、測試你的arm-elf-gcc編譯器
6、執行你的hello程序
7、編譯並運行uClinux-dist-20030909.tar.gz
8、加入網絡功能
9、安裝完成SkyEye後,下一步將做什麼?


1、什麼是SkyEye?

SkyEye是開源軟件的一個項目,SkyEye的目標是在Linux和Windows操作系統裡提供一個完全的仿真環境。SkyEye仿真環境相當於一個嵌入式計算機系統,你可以在SkyEye裡運行一些嵌入式Linux操作系統,如ARMLinux,uClinux,uc/OS-II(ucos-ii)等,並能分析和調試它們的源代碼。

如果你想知道關於SkyEye和嵌入式系統更詳細的信息,請訪問下面的站點:
www.SkyEye.org
http://www.skyeye.org/index_cn.html

通過SkyEye能仿真下面的硬件:
CPU核心:ARM7TDMI, ARM720T, ARM9, StrongARM, XScale
CPU: Atmel AT91/X40, Cirrus CIRRUS LOGIC EP7312, Intel SA1100/SA1110, Intel XScale PXA 250/255, CS89712, samsung 4510B, samsung 44B0(還不全)
內存: RAM, ROM, Flash
周邊設備: Timer, UART, ne2k網絡芯片, LCD, 觸摸屏等

目前能在SkyEye上運行下面的操作系統和系統軟件:
uC/OSII-2.5.x(支持網絡)
uClinux(基於Linux2.4.x內核, 支持網絡)
ARM Linux 2.4.x/2.6.x
lwIP on uC/OSII
基於uC/OSII, uClinux, ARM Linux的應用程序


2.SkyEye可以做什麼事情?
1. 通過SkyEye可以幫助促進嵌入式系統的學習,在不需要額外硬件的情況下學習和分析uclinux操作系統和其它嵌入式操作系統,如ucosII等。
2. SkyEye可用於嵌入式系統的教學。
3. 希望通過skyeye促進操作系統的研究,如ucosII,uclinux+RTAI,uclinux2.5.x等。
4. 可以基於SkyEye進行仿真特定硬件模塊的研究。
5. SkyEye可以作為嵌入式集成開發環境開發嵌入式系統(當然需要對SkyEye做大量的工作)。
註:引自陳渝《SkyEye Project FAQ》


3、安裝SkyEye

到http://gro.clinux.org/projects/skyeye/下載skyeye-0.7.0.tar.bz2包:

tar jxvf skyeye-v0.7.0.tar.bz2

進入解壓後的skyeye目錄,如果SkyEye的版本低於0.6.0,則運行下面的命令:

./configure --target=arm-elf --prefix=/usr/local --without-gtk-prefix --without-gtk-exec-prefix --disable-gtktest

如果SkyEye的版本高於0.6.0,則運行下面的命令:

./configure --target=arm-elf --prefix=/usr/local

接下來執行:

make
make install

安裝完成後執行skyeye

注意:
a.如果你使用的是Mandrake Linux發行版,那麼你在編譯SkyEye時遇到錯誤,並且錯誤與readline, ncurse, termcap等有關,你可以試試下面的方法:

ln -s /usr/include/ncurses/termcap.h /usr/local/include/termcap.h

接著再make和make install看能否成功!
b.如果你的Linux發行版是Debian Linux,那麼不要使用gcc 2.95或是gcc 3.0,請使用gcc 3.2+
c.gcc的版本要在2.96或以上
d.如果SkyEye的版本大於0.6.0,那麼使用LCD仿真需要在Linux系統裡安裝GTK軟件。


4、安裝arm-elf交叉編譯器

下載arm-elf-tools-20030314.sh
ftp://166.111.68.183/pub/embed/uclinux/soft/tools/arm
或到
ftp://166.111.8.229/OS/Embeded

執行:
chmod a+x arm-elf-tools-20030314.sh
然後:
./arm-elf-tools-20030314.sh

ls /usr/local/bin/

你應能看到以arm-elf開頭的可執行文件,其中arm-elf-gcc就是用來編譯你目標平台的編譯器的,當然還有一些小工具,後面將一一講來。


5、測試你的arm-elf-gcc編譯器

先寫一個小程序hello.c


#include

int main(void)
{
int i;

for(i = 0; i < 6; i++){

printf("i = %d ",i);

printf("Hello, embedded linux!\n");
}

return 0;
}


然後執行:

arm-elf-gcc -Wl,-elf2flt -o hello hello.c

-elf2flt參數是將elf文件格式轉為flat文件格式,這個工具是在你安裝交叉編譯器產生的。

或者你可以寫個Makefile文件,執行:

make

這裡是我的Makefile文件,僅供參考:

# begin

CC = arm-elf-gcc

CFLAGS = -D__PIC__ -fpic -msingle-pic-base -O2 -pipe -Wall -g
LDFLAGS = -Wl,-elf2flt

LIBS =
OBJS = hello.o

all: hello

hello: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o hello $(OBJS) $(LIBS)

clean:
rm -rf *.o *.elf *.gdb hello

# end


如果編譯通過,就會產生hello可執行文件。用下面的命令:

file hello

你會發現,它是BFLT(binary FLAT),你目標平台所支持的文件格式。


6、執行你的hello程序

這裡,我們將借助genromfs這個小工具來完成測試,這個工具就是你在安裝交叉編譯器時產生的,你可以直接使用它。

到http://gro.clinux.org/projects/skye...0.4.tar.bz2包:

tar jxvf skyeye-binary-testutils-1.0.4.tar.bz2

cd testsuits/at91/uclinux2(當然你還可以用別的)

mkdir romfs(建一個目錄,後面用)

mount -o loop boot.rom /mnt/xxx

cp -r /mnt/xxx/* romfs

另外,把你編譯好的可執行程序拷貝到/romfs/bin目錄裡,這裡就是hello了!

genromfs -f boot.rom -d romfs/

註:可以用genromf -h來獲得幫助!

OK!執行下面的命令:

skyeye linux

(skyeye)target sim

(skyeye)load

(skyeye)run

kernel start.....

很熟悉了吧。。。

cd /bin

hello

可以看到結果了嗎?

其實到了這一步,你就可以開發自己的程序了!


7、編譯並運行uClinux-dist-20030909.tar.gz

到ftp://166.111.68.183/pub/embed/uclinux/soft/
或到ftp://166.111.8.229/OS/Embeded/uclinux/pub/uClinux/dist下載
uClinux-dist-20030909.tar.gz

假設把它下載到/usr/src/目錄下,然後依次執行下面的命令:

tar zxvf uClinux-dist-20030909.tar.gz
cd uClinux-dist/

在圖形方式下可用命令make xconfig

在命令行方式下用命令make menuconfig

vendor/product中選擇GDB/ARMulator
kernel版本選擇2.4
然後save and exit

運行下面這兩條命:
make dep
make

此時在/usr/src/uClinux-dist/linux-2.4.x目錄下會生成可執行文件linux
在/usr/src/uClinux-dist/images/會生成romfs.img等文件

在uClinux-dist目錄下建立仿真AT91的skyeye配置文件skyeye.conf,內容如下:
cpu: arm7tdmi
mach: at91
mem_bank: map=M, type=RW, addr=0x00000000, size=0x00004000
mem_bank: map=M, type=RW, addr=0x01000000, size=0x00400000
mem_bank: map=M, type=R, addr=0x01400000, size=0x00400000, file=images/romfs.img
mem_bank: map=M, type=RW, addr=0x02000000, size=0x00400000
mem_bank: map=M, type=RW, addr=0x02400000, size=0x00008000
mem_bank: map=M, type=RW, addr=0x04000000, size=0x00400000
mem_bank: map=I, type=RW, addr=0xf0000000, size=0x10000000


這個時候就可以用skyeye來調試運行kernel了,在/usr/src/uClinux-dist執行如下命令:

skyeye linux-2.4.x/linux

(skyeye)target sim

(skyeye)load

(skyeye)run

kernel start.....

注意:
要在skyeye.conf所在目錄下執行skyeye linux-2.4.x/linux


8、加入網絡功能
a.用root用戶進行操作。
b.你要看你的/lib/modules/'uname -r'/kernel/drivers/net/目錄裡有沒有tun.o
如果沒有的話你就需要編譯你的linux內核來獲得tun.o了。
c.(1)運行tun設備模塊:

#insmod /lib/modules/'uname -r'/kernel/drivers/net/tun.o

如果你沒有該設備,那你就要用下面的命令來創建它:

#mkdir /dev/net
#mknod /dev/net/tun c 10 200

(2)運行vnet(虛擬集線器)設備模塊(這一步不是必需的):
獲取vnet的源碼,然後創建設備:

#mknod /dev/net/vnet c 10 201
#chmod 666 /dev/net/vnet

創建vnet.o
#make vnet.o

插入模塊vnet.o
#insmod vnet.o

進入test目錄,用test來測度vnet.o
#cd test
#make
#./testvnet1

d.配置skyeye.conf文件

cpu: arm7tdmi
mach: at91
mem_bank: map=M, type=RW, addr=0x00000000, size=0x00004000
mem_bank: map=M, type=RW, addr=0x01000000, size=0x00400000
mem_bank: map=M, type=R, addr=0x01400000, size=0x00400000, file=images/romfs.img
mem_bank: map=M, type=RW, addr=0x02000000, size=0x00400000
mem_bank: map=M, type=RW, addr=0x02400000, size=0x00008000
mem_bank: map=M, type=RW, addr=0x04000000, size=0x00400000
mem_bank: map=I, type=RW, addr=0xf0000000, size=0x10000000
# format: state=on/off mac=xx:xx:xx:xx:xx:xx ethmod=tuntap/vnet hostip=dd.dd.dd.dd
net: state=on, mac=0:4:3:2:1:f, ethmod=tun, hostip=10.0.0.1


下面將對上面的一些參數作下說明:
state=on/off意思是仿真的NIC(網絡接口板)是有線的還是無線的;
mac=仿真適配器的MAC地址;
ethmod=tuntap/vnet在主機環境裡使用的虛擬設備;
hostip=意思是主機環境與keyeye交互用的IP
格式: state=on/off mac=xx:xx:xx:xx:xx:xx ethmod=tuntap/vnet hostip=dd.dd.dd.dd

For example:
#set nic info state=on/off mac=xx:xx:xx:xx:xx:xx ethmod=tuntap/vnet hostip=dd.dd.dd.dd
net: state=on, mac=0:4:3:2:1:f, ethmod=tun, hostip=10.0.0.1

net: state=on, mac=0:4:3:2:1:f, ethmod=vnet, hostip=10.0.0.1

注意:
如果你想在同一時刻運行兩個或更多的skyeye,那麼請為每一個skyeye使用不同的skyeye.conf

e.運行skyeye linux-2.4.x/linux

9、安裝完成SkyEye後,下一步將做什麼?

1、對於嵌入式操作系統的初學者和入門者和入門的學生而言,他們可以先看一些有關操作系統和嵌入式操作系統方面的教材和書籍,如與uC/OS、Minix、uClinux、Linux相關的書籍等。然後可以在Skyeye上開發一些簡單的應用程序例子(如進程間通信、進程優先級、死鎖情況、網絡應用等),對某些操作系統功能(如進程調度、內存管理、網絡子系統、文件子系統等)進行簡單的修改和擴展,並通過Skyeye進行運行和調試,看看會發生什麼情況。

2、對於有一定經驗的軟件工程師而言,在SkyEye上完成一定的應用系統原型開發是值得一做的事情。比如移植或開發一個文件子系統或網絡子系統到一個特定的操作系統中,相信比在一個真實的開發板上開發要容易一些。在Skyeye上進行一些操作系統的移植和開發(如移植RTLinux、RTAI等其它操作系統到Skyeye上)也是很有挑戰性的工作。

3、對於硬件工程師而言,對Skyeye進行擴充,設計新的硬件仿真(如USB、IDE硬盤等)使得Skyeye的硬件仿真功能更加強大,支持更多功能的軟件,是很有意義的事情。

參考:
SkyEye項目站點裡的一篇中文文檔;
陳渝《SkyEye Project FAQ》;
skyeye-0.7.0中的README文檔。

後記:
為了讓大家能快速上手,進行實際的開發工作,我趕湊了一篇文檔,很粗糙。但我堅信隨著更多的有經驗的人的加入;隨著我們自己水平的提高,一定會出現更多、更好的文章來。就讓我們快點行動起來吧!

最後,我再次建議大家看一下《嵌入式Linux技術與應用》這本書。

可以到http://www.skyeye.org/document.htm或是
ftp://166.111.68.183/pub/embed/skyeye/document/或是
http://www.huihoo.org/mirrors/skyeye/
下載文檔,可以獲得更多有關skyeye和嵌入式Linux開發的知識和經驗。

[技術] 一步一步教你在skyeye上運行uboot

Written on 9:00 上午 by Yu Lai

[轉自Experiences of Code (http://blog.csdn.net/zblue78)]

一步一步教你在skyeye上運行uboot
by faif

1. 簡介

skyeye是一個很好的,基於各種ARM系列CPU的,SOC和主板級的模擬器。uboot是一個可以在各種cpu(arm,mips,powerpc)的主板上運行的引導程序,相當於PC機的BIOS但是又遠遠的強於普通的BIOS,比如支持網絡引導,引導各種內核,甚至一個簡單的shell,等等。他們兩個都是基於GPL的開源自由軟件。

這篇文章教你怎樣在最少量的修改代碼的情況下,用skyeye模擬EP7312並在上面運行uboot,給接觸嵌入系統的新手一個感性的認識。

2. 建立開發環境

2.1 skyeye模擬器的安裝

開發環境是建立在Linux上的。首先下載安裝skyeyes-0.8.5.1的源代碼包,解壓,按照裡面的readme安裝,注意你的linux要有gtk的支持。安裝的時候要以root的身份。在各種linux發行版下的安裝注意事項參照論壇的相關帖子。安裝成功以後,把skyeye的目標目錄加入你的路徑,這樣你就可以在任何目錄下執行skyeye模擬器了。

2.2 交叉編譯器的安裝

交叉編譯器是運行在主機上編譯另外一種體系結構的編譯器。比如,我的主機是linux在x86上,我現在要編譯基於ARM的代碼,所以我就不能用普通的編譯器而需要交叉編譯器。我曾經試過自己從gcc的源代碼構建交叉編譯器,很麻煩和耗時。uboot的作者同樣也開發了一個很好的交叉編譯器叫ELDK(Embedded linux development kit)。我就使用這個,當然你也可以使用其他嵌入式公司提供的。你可以從以下的網址查看提供ELDK的鏡像:

ELDK Availability: http://www.denx.de/twiki/bin/view/DULG/ELDKAvailability

ELDK有三個版本分別編譯MIPS,PPC和ARM。我從下面的鏡像下載了基於ARM的交叉編譯器:

http://sunsite.utk.edu/ftp/pub/linux/eldk/3.1/arm-linux-x86/iso/

文件為"arm-2004-11-09.iso",它支持ARM7, ARM9, XScale, AT91RM9200 and other ARM based systems。

安裝交叉編譯器,我將交叉編譯器安裝到自己的目錄「/opt/x86-arm/」裡面:
/mnt/cdrom/install -d /opt/x86-arm/

等待安裝結束以後設置好用戶環境:


export PATH="${PATH}:/opt/x86-arm/usr/bin:/opt/x86-arm/bin"
export CROSS_COMPILE=arm-linux-

這樣你在任何目錄也可以訪問交叉編譯器了。

測試:

arm-linux-gcc -o testarm test.c
file testarm
testarm: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped.

說明你編譯好的文件是ARM上的代碼了。你可以用arm-linux-gcc來編譯你的文件了。

3. 修改Uboot

從uboot的網站上可以下載到最新的uboot源代碼,你可以從以下的網址下載

http://u-boot.sourceforge.net/
ftp://ftp.denx.de/pub/u-boot/

uboot的源碼結構清晰,註釋詳細,是學習嵌入系統的很好的例子。我下載的是最新的U-Boot-1.1.2。因為我們要模擬EP7312的芯片,而uboot已經支持一個基於EP7312的板子了,所以我們只要對uboot裡面有關EP7312的板子的配置略作修改就可以了。uboot裡面有關主板的配置文件都在"include/configs/.h"下,所以我們找到include/configs/ep7312.h,對它進行修改。

找到
#define CONFIG_DRIVER_CS8900 1
改為
#define CONFIG_DRIVER_CS8900 0

找到
#define CONFIG_COMMANDS (CONFIG_CMD_DFL | CFG_CMD_JFFS2)
改為
#define CONFIG_COMMANDS (CONFIG_CMD_DFL) /*Skyeye doesn't have jffs2*/


然後回到uboot的根目錄下,配置,編譯:

make ep7312_config
make all

等待結束以後我們會發現u-boot.bin和u-boot兩個文件,其中u-boot.bin是raw的二進制文件。u-boot是ELF格式的。

4. 配置skyeye,並運行uboot

首先,新建一個目錄代表你的EP7312的主板。這樣也可以保持文件的清潔和有序。

mkdir board01

將你剛才編譯成功的u-boot.bin拷貝到這個目錄下來。skyeye支持raw binary和ELF的格式,這裡我們用raw binary的格式。

編輯skyeye.conf,這個文件是用來配置主板的,詳細說明見skyeye的相關文檔。我的skyeye.conf如下:

#skyeye config file for uboot
cpu: arm720t
mach: ep7312

mem_bank: map=I, type=RW, addr=0x80000000, size=0x00010000

#skyeye for uboot flash 16M bank 1
mem_bank: map=M, type=RW, addr=0x00000000, size=0x01000000, file=./u-boot.bin,boot=yes

#skyeye for uboot sdram 16m bank 1
mem_bank: map=M, type=RW, addr=0xc0000000, size=0x01000000

注意這裡的內存的地址和容量的分配都是根據uboot裡面的ep7312的配置文件調整的。這樣也可以是我們對uboot的代碼修改做到最小。

這時候你的skyeye-ep7312主板就配置好了。你可以試著運行了。在你現在的目錄下打入:
skyeye

然後在skyeye的界面下打入:

target sim
run

這時候你可以看到uboot的啟動界面,和提示符,如果你鍵入「hlep」,可以查看所有uboot支持的命令,鍵入「version」可以查看當前uboot的版本,等等。

5. 進一步的工作

由於本文是最基本的介紹性的一步一步的指導。有很多工作還要去嘗試。比如:

× 由於現在skyeye還不支持flash內存,所以我們是不是可以修改uboot上ep7312相對flash的代碼來臨時滿足我們的需要,不然的話,對於在uboot上面的環境參數的設置,我們只能去修改源碼裡面的缺省參數。

× Uboot支持8019AS的以太網控制器,skyeye也支持了這個硬件的模擬,我們要進一步的使uboot的網絡也在skyeye上用起來?

× 對於模擬flash的開發也可以用uboot來測試。uboot裡面各種板子有大量的flash驅動程序。

還望各位高手指教,我進一步修改和提高。