最近因為需要,所以必須使用到shared memory,一看之下,越是覺得shared memory其實是相當有趣的一種構想,只會存在於特定的平台,強烈跟作業系統相關,雖然說很多系統都支援這樣的實做
很多人都知道process有自己獨立的virtual memory space,這也是MMC存在的價值,但是在沒有multi-thread的時候,兩個process分享資料有其需求,這時候就必須透過shared memory,可是這塊shared memory應該屬於誰的?我想應該是OS"作弊"在某個記憶體區間弄出了一塊空間,這塊空間是屬於作業系統管理的,雖然他還是有權限的控管,但是他的存在不屬於單一process
最後回到為何我需要利用到shared memory,我先前想要建立兩個server,這兩個server位於同一台機器,但是兩者的狀態可能會互相影響,共同的資料分享是必須得,如果只是兩個工作這件事情可以用multi-thread來解決,multi-thread在race condition的控管比multi-process容易許多,也好的多,很不幸,因為是server,server會呼叫accept這個system call,意味著system call將會block住整個process,導致另外一個server/thread無法正常運作,這時候只好轉向multi-process
當然還有其他更複雜的手法,比方說使用interval socket來建立process之間的連線,但是複雜度會比shared memory高,pipe的方式則是效能略遜,如果其他網友有更好的方式請建議我
2012年2月29日 星期三
itoa找不到@@a
這個ANSI C的function竟然linker找不到,十分讓我訝異,後來我改用sprintf來達成,由網路上找來的資訊,看來我也不是第一人
http://stackoverflow.com/questions/5242524/converting-int-to-string-in-c
http://stackoverflow.com/questions/5242524/converting-int-to-string-in-c
IPC中的shared memory管理
其實寫shared memory很常遇到的一個問題是,空間已經配置,但是因為程式的不正常結束,導致空間尚未歸還,下次再用同樣的key要申請的時候引發File exists的錯誤
使用
ipcs -t
ipcs -m
他會列出所有shared memory的列表
ipcrm -m 393228
最後一個是列表上的share memory id,表示將該區域清空
參考資料:
http://telinit0.blogspot.com/2009/09/clearing-shared-memory-of-crashed.html
使用
ipcs -t
ipcs -m
他會列出所有shared memory的列表
ipcrm -m 393228
最後一個是列表上的share memory id,表示將該區域清空
參考資料:
http://telinit0.blogspot.com/2009/09/clearing-shared-memory-of-crashed.html
2012年2月28日 星期二
兩個servers同時存在一個process的難題
因為兩個server必須要分享一些資料,這樣一來變成multi-process似乎是唯一的選擇,因為如果一旦使用multi-thread實作,勢必因為accept()這個function call block住整個process,即使使用epoll也是無法解決
"建構嵌入式Linux系統"讀後感
基本上真的是一本沒話說的入門好書了,作者是實作經驗豐富的人,所以中間穿插了很多實務上的考量與建議。如果想要讀這本的書人,建議先把linux開機流程看懂,讀起來更有概念,更輕鬆。此書稍微美中不足的只有整個偏重概念介紹,整體架構很好,但沒有完整的一個實例,可能讓人沒有從頭到尾操作的感覺,如果要體驗真的得找張板子好好實作一番
[好書推薦]建構嵌入式Linux系統(ch10, 11)
chapter 10提到了許多server架設的方式,也討論了許多必要注意的細節,很多部分端看應用
總結幾個重點:
作者大致上介紹了幾個常見的server,也提出了一些替代方案跟注意事項,我稍微簡介一下
現在愈來愈多的嵌入式產品其實已經開始使用web-GUI替嵌入式系統作為設定的介面(尤其是網路產品),所以我想如何把script language引入是一個必要的選擇
文章相當偏重實用以及經驗,所以實際編譯過應該比較有絕
chapter 11
文章中提到了一些除錯工具,最出名的當然就是gdb囉,還有一些系統的側寫(profile)工具,以及效能分析工具,gdb跟ptrace、strace網路上資料已經非常多,我個人經驗有限,就不再這裡野人獻曝了
最後幾章開始進入了即時系統的部分,放眼望去目前大多是一般的嵌入式系統,即時系統的部分比較少,等有遇到再說吧,看到這裡,可以把這本書蓋上了,有需要的時候再回來看
總結幾個重點:
- Size does the matter. 有必要注重daemon的體積大小,不過也是看應用面,小記憶體就要小心,strip是個好幫手
- 需要的library要拷貝到,編譯的時候要慎選library,可以省略不必要的lib,如果可以,考慮uclibc
作者大致上介紹了幾個常見的server,也提出了一些替代方案跟注意事項,我稍微簡介一下
- SNMP : Net-SNMP用來建立SNMP-agent至於嵌入式裝置不錯
- Telnet : utelnetd是個不錯的選擇,如果用uclilbc,記得加上termcap.h,可以是空檔案,另外裝置檔案記得要建立,如mknod /dev/ptmx c 5 2
- SSH : dropbear是個替代方案
- HTTP : bao或者thttpd
- DHCP : udhp是一個替代方案,要記得加上udhcpd.leases檔案
現在愈來愈多的嵌入式產品其實已經開始使用web-GUI替嵌入式系統作為設定的介面(尤其是網路產品),所以我想如何把script language引入是一個必要的選擇
文章相當偏重實用以及經驗,所以實際編譯過應該比較有絕
chapter 11
文章中提到了一些除錯工具,最出名的當然就是gdb囉,還有一些系統的側寫(profile)工具,以及效能分析工具,gdb跟ptrace、strace網路上資料已經非常多,我個人經驗有限,就不再這裡野人獻曝了
最後幾章開始進入了即時系統的部分,放眼望去目前大多是一般的嵌入式系統,即時系統的部分比較少,等有遇到再說吧,看到這裡,可以把這本書蓋上了,有需要的時候再回來看
2012年2月27日 星期一
[好書推薦]建構嵌入式Linux系統(ch9)
很難讀懂得一章,無法完全理解U-boot是如何運作的,看了許久跟著在網路上爬文許久,突然領悟到了
u-boot的工作主要就是載入kernel,然後讓kernel去載入init這個process,這兩個元件主要都是放到記憶體。先說幾種主要的載入kernel的方式
以上幾個方式可以從底下網址找到(還有很多開機指令,需要看過一下,心裡有個底)
http://www.flatws.cn/article/program/shell/2011-02-23/14038.html
仔細觀察uboot提供的指令就可以理解到需要哪些環境變數,這些環境變數主要是記載了載入kernel的實體位置以及rootfs所在的地方
第一種載入的方式
必須先把kernel轉成為適當地格式,kernel一般會被壓縮成為zImage格式,然後在被變成uImage放置到flash的適當位址,這個位址被需傳給uboot讓他找到,跟著就是kernel的entry point位址,很不幸的書中沒說,直接跳到實際例子,我網路爬文許久終於找到
http://tech.ddvip.com/2008-09/122095341662350.html
http://sakbk.pixnet.net/blog/post/30892645-zimage-component
其中一段提到,在arch/arm/Makefile文件中的textaddr-y:= 0xC0008000这个是内核启动的虚拟地址,由編譯的核心裡面可以挖出來。我想這點也是之前網路教材沒寫出來,遇到的麻煩處(chapter 4)。在知道這個位址之後就剩下rootfs的位置需要被理解,比如是/dev/mtdblock4,這點在前兩三章有提到過
u-boot的工作主要就是載入kernel,然後讓kernel去載入init這個process,這兩個元件主要都是放到記憶體。先說幾種主要的載入kernel的方式
- 由flash載入,最是單純,最好控制
- 由網路載入,蠻複雜的
- 由serial port載入,很龜速,主要依賴loadb/loadx/loady由serial port載入
以上幾個方式可以從底下網址找到(還有很多開機指令,需要看過一下,心裡有個底)
http://www.flatws.cn/article/program/shell/2011-02-23/14038.html
仔細觀察uboot提供的指令就可以理解到需要哪些環境變數,這些環境變數主要是記載了載入kernel的實體位置以及rootfs所在的地方
第一種載入的方式
必須先把kernel轉成為適當地格式,kernel一般會被壓縮成為zImage格式,然後在被變成uImage放置到flash的適當位址,這個位址被需傳給uboot讓他找到,跟著就是kernel的entry point位址,很不幸的書中沒說,直接跳到實際例子,我網路爬文許久終於找到
http://tech.ddvip.com/2008-09/122095341662350.html
http://sakbk.pixnet.net/blog/post/30892645-zimage-component
其中一段提到,在arch/arm/Makefile文件中的textaddr-y:= 0xC0008000这个是内核启动的虚拟地址,由編譯的核心裡面可以挖出來。我想這點也是之前網路教材沒寫出來,遇到的麻煩處(chapter 4)。在知道這個位址之後就剩下rootfs的位置需要被理解,比如是/dev/mtdblock4,這點在前兩三章有提到過
第二種由網路載入的方式
這個就頭大了,因為牽涉的東西往往很多,搞得我頭昏眼花,例如書中提到了如何建立NFS、DHCP 跟TFTP server就寫了一大段,這裡先跳過。接下來先看看開機指令
基本上就是經由TFTP Server載入kernel,然後在透過NFS server來掛載rootfs
用兩個指令達到這件事情
bootp:如何知道kernel image的位置是藉由DHCP server告知的,跟著uboot再去DNS server通知的地點利用tftp指令取回image
bootm:必須知道kernel第一條程式位址(entry address),如第一種方式提到
跟著就是設定一堆NFS相關的資料,讓uboot通知kernel如何去掛載rootfs,也就是書中看到的setenv bootnfs ... 那一堆龐雜的dscript
bootp還有變形的方式,也就是直接使用tftpboot 01000000 /data/kernel/virtex4-vmlinux-2.6.x.img表示直接透過tftp server將image抓回來放入ram 0100000的位址,這裡省略的DHCP的告知,但是就必須要事先設定好IP address,可以參考底下範例
http://blog.chinaunix.net/uid-20340721-id-1964603.html
第三種我就不介紹了,我也不打算使用:P不過如果有心的人應該看一下,哪天嵌入式系統被放到沒有網路的地方,剛好板子上面的flash又掛點,這時候只剩下你的NB跟serial port,這招看來就是最後一招了(還是可以用ICE解救??)
書中往往還教導使用如何將kernel寫到flash,還有如何編制uImage,如何架設server,讓整章看起來亂無章法,又一下子跳到實例應用,往往讓人十分迷惑
最後還有提到如何更新uboot跟image,還是不失本書一貫的風格相當精彩
最後附上其他參考資料
http://www.flatws.cn/article/program/shell/2011-02-23/14038.html
http://blog.xuite.net/chipinwu/blog/28718850-U+Boot%E7%9A%84%E9%85%8D%E7%BD%AE%E5%8F%8A%E7%B7%A8%E8%AD%AF
http://blog.udn.com/Howl0730/2663521
http://flykof.pixnet.net/blog/post/22975136-u-boot%E6%8C%87%E4%BB%A4
bootp還有變形的方式,也就是直接使用tftpboot 01000000 /data/kernel/virtex4-vmlinux-2.6.x.img表示直接透過tftp server將image抓回來放入ram 0100000的位址,這裡省略的DHCP的告知,但是就必須要事先設定好IP address,可以參考底下範例
http://blog.chinaunix.net/uid-20340721-id-1964603.html
第三種我就不介紹了,我也不打算使用:P不過如果有心的人應該看一下,哪天嵌入式系統被放到沒有網路的地方,剛好板子上面的flash又掛點,這時候只剩下你的NB跟serial port,這招看來就是最後一招了(還是可以用ICE解救??)
書中往往還教導使用如何將kernel寫到flash,還有如何編制uImage,如何架設server,讓整章看起來亂無章法,又一下子跳到實例應用,往往讓人十分迷惑
最後還有提到如何更新uboot跟image,還是不失本書一貫的風格相當精彩
最後附上其他參考資料
http://www.flatws.cn/article/program/shell/2011-02-23/14038.html
http://blog.xuite.net/chipinwu/blog/28718850-U+Boot%E7%9A%84%E9%85%8D%E7%BD%AE%E5%8F%8A%E7%B7%A8%E8%AD%AF
http://blog.udn.com/Howl0730/2663521
http://flykof.pixnet.net/blog/post/22975136-u-boot%E6%8C%87%E4%BB%A4
2012年2月26日 星期日
beagleboard xm的解析度設定
根據網站的spec寫的,他支援1280x720是可以的(再上去有問題,不過網路上印象中有人號稱hack了chip pin腳的信號,可以上到更高的解析度),但是很多人裝了ubuntu的image之後,解析度設定的問題一大堆,我自己本身也是,其實我猜應該是uboot的設定檔案讀取出了問題
很多設定為
1024x768MR-16@60
這樣之類的,我這樣設定也會出問題,最簡單的設定應該是
hd720
這個設定最沒有問題,他是1280x720MR-32@60,其他解析度呢?其實直接寫
1024x768@60
反而可以,但是如何設定16bits或者24bits我倒是不知道了(這個預設為32bits)
很多設定為
1024x768MR-16@60
這樣之類的,我這樣設定也會出問題,最簡單的設定應該是
hd720
這個設定最沒有問題,他是1280x720MR-32@60,其他解析度呢?其實直接寫
1024x768@60
反而可以,但是如何設定16bits或者24bits我倒是不知道了(這個預設為32bits)
[好書推薦]建構嵌入式Linux系統(ch8)
這個章節作者談到了各種rootfs的type,也就是要使用ext2、ext3、cramfs、squashfs、JFFS2、tmpfs哪一種,每一種都有它的特色
為了執行方便,在開發完成rootfs的時候會將他們壓縮成image或者其他格式,再載入的時候,再由kernel指示CPU該檔案解開並且掛載成root directory
另外介紹了兩種常用來拷貝rootfs到需要的地方(比方說成為image),這兩個公用程式分別是dd以及cpio,兩者都是低階壓縮工具,dd很適合將整個partition拷貝到另外一個地方,比如另外一個partition,cpio則是連device file都能拷貝
geneextfs -b 1024 -d rootfs -D device_table.txt -e 0 initrd.img
gzip -9 < images/initrd.img > images/initrd.bin
第一行先產生一個ext2的image跟著第二行將image壓縮成ramdisk
cd ${PROJECTDIR}/rootfs
find . | cpio -o -H newc | gzip > {PROJECTDIR}/initramfs.cpio.gz
這樣kernel會把這個檔案已tmpfs的格式放到ramdisk下執行
書本中還提到了許多跟實務上有相關的選擇標準,礙於個人接觸太少,只好先"記帳":P
最後做整提到了更新的問題,更新的情況也會影響rootfs type的選擇,另外就是如何處理更新的過程,有三個作者提到的基本工具 : cpio、rsync以及dpkg
為了執行方便,在開發完成rootfs的時候會將他們壓縮成image或者其他格式,再載入的時候,再由kernel指示CPU該檔案解開並且掛載成root directory
另外介紹了兩種常用來拷貝rootfs到需要的地方(比方說成為image),這兩個公用程式分別是dd以及cpio,兩者都是低階壓縮工具,dd很適合將整個partition拷貝到另外一個地方,比如另外一個partition,cpio則是連device file都能拷貝
geneextfs -b 1024 -d rootfs -D device_table.txt -e 0 initrd.img
gzip -9 < images/initrd.img > images/initrd.bin
第一行先產生一個ext2的image跟著第二行將image壓縮成ramdisk
cd ${PROJECTDIR}/rootfs
find . | cpio -o -H newc | gzip > {PROJECTDIR}/initramfs.cpio.gz
這樣kernel會把這個檔案已tmpfs的格式放到ramdisk下執行
書本中還提到了許多跟實務上有相關的選擇標準,礙於個人接觸太少,只好先"記帳":P
最後做整提到了更新的問題,更新的情況也會影響rootfs type的選擇,另外就是如何處理更新的過程,有三個作者提到的基本工具 : cpio、rsync以及dpkg
I/O效能
隨著人類的貪心追求效能,IO開始必須要滿足種種的需求,比方說asynchronous I/O或者non-blocking I/O等等,甚至已經延燒到kernel的部分,新的I/O function往往就是為了這樣的需求開發出來的,如何減少等待時間,儘快回應使用者的需求
參考資料
http://daydreamer.idv.tw/rewrite.php/read-46.html
http://dev.gameres.com/Program/Control/IOCP.htm
參考資料
http://daydreamer.idv.tw/rewrite.php/read-46.html
http://dev.gameres.com/Program/Control/IOCP.htm
2012年2月25日 星期六
[好書推薦]建構嵌入式Linux系統(ch7)
這一章節講到Memory Technology Device (MTD),主要針對Nor flash跟Nand flash作說明,因為這兩者是經常被使用在遷入系統的對象
在編譯核心的時候必須要將兩者需要的driver編譯進入模組(在menuconfig中的Device Drivers=>MTD Support=>...這些項目)
作者也提供了一系列操作MTD的工具程式,如一般工具
flash_info、flash_erase、flash_unlock、flash_ereaseall、flash_lock、flashcp、doc_loadbios、mtd_debug
FTL(flash translation layer)與NFTL(nand FLT)工具
ftl_format、ftl_check、nftl_format、nftldump
如何格式化成適當地檔案格式(jffs與jffs2)
mkfs.jffs2、sumtool、jffs2dump
另外重要的就是如何分割flash這類的MTD,因為很有可能就是要將檔案系統等等資料寫入這個地方。在操作上linux大致上將一切裝置看成檔案,而所有檔案大致上分為兩類block以及character,所有的裝置必須在/dev底下可以被找到
所以除了如何利用工具分割flash之外就是如何用mknod建立適當地裝置,給予primary number與secondary number
目前比較沒有感覺,如果等到講到bootloader以及如何在裝置上面燒錄bootloader、kernel跟rootfs應該會比較有意義
在編譯核心的時候必須要將兩者需要的driver編譯進入模組(在menuconfig中的Device Drivers=>MTD Support=>...這些項目)
作者也提供了一系列操作MTD的工具程式,如一般工具
flash_info、flash_erase、flash_unlock、flash_ereaseall、flash_lock、flashcp、doc_loadbios、mtd_debug
FTL(flash translation layer)與NFTL(nand FLT)工具
ftl_format、ftl_check、nftl_format、nftldump
如何格式化成適當地檔案格式(jffs與jffs2)
mkfs.jffs2、sumtool、jffs2dump
另外重要的就是如何分割flash這類的MTD,因為很有可能就是要將檔案系統等等資料寫入這個地方。在操作上linux大致上將一切裝置看成檔案,而所有檔案大致上分為兩類block以及character,所有的裝置必須在/dev底下可以被找到
所以除了如何利用工具分割flash之外就是如何用mknod建立適當地裝置,給予primary number與secondary number
目前比較沒有感覺,如果等到講到bootloader以及如何在裝置上面燒錄bootloader、kernel跟rootfs應該會比較有意義
2012年2月24日 星期五
[好書推薦]建構嵌入式Linux系統(ch6)
作者介紹了一個rootfs需要的各個目錄
依照作者的說法有許多目錄可以省略,但我倒是抱持著相反看法,雖然很多目錄不實用(依照應用可省略),但是如果只是空的目錄應該是不占用空間,放著應該也不至於佔據太大的空間。
跟著提到了拷貝必要的libs進入rootfs之中,他列出了兩個重要的script可以拷貝需要的static & dynamic library
接著提到核心module的拷貝跟kernel image,其實作者也提到kernel image會不會進入rootfs跟bootloader有關,以現在很多小型的記憶體裝置或者flash memory,應該都還是有可能不會進入rootfs
跟著作者相當程度的介紹了/dev跟/udev底下的功能以及設定,尤其是udev,這是一個在2.6後引入的hotplug以及新的系統控制的目錄,相當重要,想來未來會愈來愈吃重,必須仔細詳讀。
最介紹到了系統初始化的啟動程式init以及其他的應用程式(只的是一般常用的公用程式如ls之類),這時候又介紹了一個重要的project,名為busybox,以體積小著稱,他同時包含了公用程式以及init的腳色,相當適合使用在遷入系統
目錄 | 內容 |
bin | 使用者指令 |
boot | boot loader的靜態檔案 |
dev | device files |
etc | 設定檔 |
home | 使用者家目錄 |
lib | 程式庫 |
media | 可移除掛載點 |
mnt | 暫時性的掛載點 |
opt | 附加軟體套件 |
proc | 核心行程資訊與虛擬檔案系統 |
root | root home目錄 |
sbin | root使用的指令 |
sys | 系統資訊以及控制的虛擬檔案系統 |
tmp | 暫時性檔案 |
usr | 第二層目錄包含對大多使用者的應用程式與文件,包含X server |
var | log,與公用程式的變動資料 |
依照作者的說法有許多目錄可以省略,但我倒是抱持著相反看法,雖然很多目錄不實用(依照應用可省略),但是如果只是空的目錄應該是不占用空間,放著應該也不至於佔據太大的空間。
跟著提到了拷貝必要的libs進入rootfs之中,他列出了兩個重要的script可以拷貝需要的static & dynamic library
接著提到核心module的拷貝跟kernel image,其實作者也提到kernel image會不會進入rootfs跟bootloader有關,以現在很多小型的記憶體裝置或者flash memory,應該都還是有可能不會進入rootfs
跟著作者相當程度的介紹了/dev跟/udev底下的功能以及設定,尤其是udev,這是一個在2.6後引入的hotplug以及新的系統控制的目錄,相當重要,想來未來會愈來愈吃重,必須仔細詳讀。
最介紹到了系統初始化的啟動程式init以及其他的應用程式(只的是一般常用的公用程式如ls之類),這時候又介紹了一個重要的project,名為busybox,以體積小著稱,他同時包含了公用程式以及init的腳色,相當適合使用在遷入系統
2012年2月23日 星期四
LCD之framebuffer(3)
還沒深入了解framebuffer之前,我實在意想不到每次在framebuffer(fb)"作畫"都會有些小瑕疵,因為如果是在X-win,大概很快的x-win的東西就會蓋過fb的內容,變得難看,在tty底下則是會面臨那個閃爍游標的問題
取消或者使用cursor blink功能
echo 0 > /sys/class/graphics/fbcon/cursor_blink
如果是1表示enable,而0表示disable
另外一個問題是sleep mode
取消lcd sleep mode(可是有時候沒這個檔案)
echo 1 > /sys/class/graphics/fb0/blank
透過一些操作來達成
echo -e '\033[9;X]' > /dev/tty1
參考資料
http://www.armadeus.com/wiki/index.php?title=FrameBuffer
2012年2月22日 星期三
[好書推薦]建構嵌入式Linux系統(ch5)
這章說的是核心(kernel)編譯
第一步是挑選核心,看是要使用哪個版本,包含考慮到硬體的考量,畢竟核心是對硬體提供了好一些driver,以及提供了各種記憶體管理、排程等功能
編譯核心的過程大致上分成幾個
編譯的時候需要先設定configure
除了第二個,基本上另外三個是一樣的,但是推薦使用make menuconfig,因為config在設定時候太麻煩,xconfig則需要x-win,且效果未必比menuconfig好
make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig
ARCH指定CPU,CROSS_COMPILE則是toolchain的名稱,千萬別傻傻跟我一樣打成CROSS-COMPILER,這樣會發稱慘案,請參考我之前編譯busybox的時候
另外作者提到一個重要的變數EXTRAVERSION,其實就是字面上的extra-version,嵌入式系統有時候會需要用上自己特殊的設定,這個變數指定最後的postfix,讓人辨別編譯出來的版本如2.6.20-motor-diff
編譯核心相對應單純
make ARCH=arm CROSS_COMPILE=arm-linux- zImage
系統會產生出vmlinux檔案,這是尚未壓縮的,zImage是使用bizp2壓縮過的,bzImage則是讓產生的壓縮檔案格式允許超出512KB,對應於硬體的限制
編譯模組也是蠻單純的
make ARCH=arm CROSS_COMPILE=arm-linux- modules
如果需要清除之前設定
make ARCH=arm CROSS_COMPILE=arm-linux- distclean
安裝核心與安裝模組
安裝核心比較單純,只要拷貝四個檔案vmlinux、System.map 、設定檔案(.config)跟bzImage/zImage,他們會位在arch/arm/boot/目錄底下。模組要小心處理,不能安裝到host機器中,不然可能會有危險
make ARCH=arm CROSS_COMPILE=arm-linux- INSTALL_MOD_PATH=${PROJROOT}/images/modules-2.6.20 modules_install
將module放到第四章提到的相對應目錄內
最後就是測試的部分,作者也沒給出太多建議,不過倒是有提到一個有趣的議題就是kernel panic的處理函式panic(),有些機器可以直接重新開機,有些則必須額外處理,相對應於應用跟規劃,例如火災的感知器如果有異狀,產生kernel panic,我想馬上產生一個警報(即使是假警報)也好過重新開機,因為重新開機有可能在當機,延誤了火災的救援
第一步是挑選核心,看是要使用哪個版本,包含考慮到硬體的考量,畢竟核心是對硬體提供了好一些driver,以及提供了各種記憶體管理、排程等功能
編譯核心的過程大致上分成幾個
- 設定congfigure
- 依照平台編譯
- 編譯模組 (modules)
- 安裝核心
- 安裝模組
- 測試核心
編譯的時候需要先設定configure
- make config : 依次要求使用者設定
- make oldconfig : 載入舊的設定檔案
- make menuconfig : 使用curses建構的window設定
- make xconfig : 使用x-win
除了第二個,基本上另外三個是一樣的,但是推薦使用make menuconfig,因為config在設定時候太麻煩,xconfig則需要x-win,且效果未必比menuconfig好
make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig
ARCH指定CPU,CROSS_COMPILE則是toolchain的名稱,千萬別傻傻跟我一樣打成CROSS-COMPILER,這樣會發稱慘案,請參考我之前編譯busybox的時候
另外作者提到一個重要的變數EXTRAVERSION,其實就是字面上的extra-version,嵌入式系統有時候會需要用上自己特殊的設定,這個變數指定最後的postfix,讓人辨別編譯出來的版本如2.6.20-motor-diff
編譯核心相對應單純
make ARCH=arm CROSS_COMPILE=arm-linux- zImage
系統會產生出vmlinux檔案,這是尚未壓縮的,zImage是使用bizp2壓縮過的,bzImage則是讓產生的壓縮檔案格式允許超出512KB,對應於硬體的限制
編譯模組也是蠻單純的
make ARCH=arm CROSS_COMPILE=arm-linux- modules
如果需要清除之前設定
make ARCH=arm CROSS_COMPILE=arm-linux- distclean
安裝核心與安裝模組
安裝核心比較單純,只要拷貝四個檔案vmlinux、System.map 、設定檔案(.config)跟bzImage/zImage,他們會位在arch/arm/boot/目錄底下。模組要小心處理,不能安裝到host機器中,不然可能會有危險
make ARCH=arm CROSS_COMPILE=arm-linux- INSTALL_MOD_PATH=${PROJROOT}/images/modules-2.6.20 modules_install
將module放到第四章提到的相對應目錄內
最後就是測試的部分,作者也沒給出太多建議,不過倒是有提到一個有趣的議題就是kernel panic的處理函式panic(),有些機器可以直接重新開機,有些則必須額外處理,相對應於應用跟規劃,例如火災的感知器如果有異狀,產生kernel panic,我想馬上產生一個警報(即使是假警報)也好過重新開機,因為重新開機有可能在當機,延誤了火災的救援
[好書推薦]建構嵌入式Linux系統(ch4.1)
以前我老是以為只要準備好toolchain就可以,但是慢慢的發現其實不只如此,但是更發現這本書竟然講得如此有系統(雖然礙於時間,提到的軟體有些已經過時)
第四章
不只是toolchain更說明了整體開發環境的規劃,作者將每一個專案例舉為一種開發工作,如何布置工作區域(目錄),顯得很重要
作者建議將建立一工作區域,然後底下有這些目錄
如果是我大概只有四五個目錄,就是重要的bootloader、kernel、file system、toolchain跟建構toolchain的工具XD
config file其實我認為包含了ㄧ些有關專案的環境變數,如果要開發數個專案應該獨立出來,避免寫入.bashrc之類的個人設定檔案中,可以保持彈性。toolchain的命名規則其實現在很多工具建構出來就會自動類似作者建議的cpu-manufacturer-kernel-os,不過很可惜也不是完全吻合,不過至少是一種建議
作者也討論到了各種lib合適的狀況,只有舉例到thread跟glibc/ulibc等,建構這樣一個環境真的還蠻費工夫的,考慮如果以後還要維護就會知道這有多麻煩,如果同一台機器/專案,要因應不同時間建構不同的kernel對應不同的toolchain,比方說gcc 2.6很難編譯得過kernel 3.x,這時候就要更新toolchain,如果沒有設定好設定案跟更新lib,恐怕功夫就不只是編譯這麼簡單
其次再考慮到要建立這些toolchain跟lib也要花上龐大的功夫,作者推薦了兩個前人努力的結果的套件,可以幫忙建立toolchain跟一些lib,分別是ptxdis跟buildroot
第四章
不只是toolchain更說明了整體開發環境的規劃,作者將每一個專案例舉為一種開發工作,如何布置工作區域(目錄),顯得很重要
作者建議將建立一工作區域,然後底下有這些目錄
- bootldr : target boot loader所在
- build-tools : 建構toolchain的工具目錄
- debug : 除錯工具
- doc : 文件
- images : 建構的image
- kernel : target kernel目錄
- project : 專案的configure file
- rootfs : target root file system
- sysapps : target applications
- tmp : temp file directory
- tools : cross platform toolchain and c libs
如果是我大概只有四五個目錄,就是重要的bootloader、kernel、file system、toolchain跟建構toolchain的工具XD
config file其實我認為包含了ㄧ些有關專案的環境變數,如果要開發數個專案應該獨立出來,避免寫入.bashrc之類的個人設定檔案中,可以保持彈性。toolchain的命名規則其實現在很多工具建構出來就會自動類似作者建議的cpu-manufacturer-kernel-os,不過很可惜也不是完全吻合,不過至少是一種建議
作者也討論到了各種lib合適的狀況,只有舉例到thread跟glibc/ulibc等,建構這樣一個環境真的還蠻費工夫的,考慮如果以後還要維護就會知道這有多麻煩,如果同一台機器/專案,要因應不同時間建構不同的kernel對應不同的toolchain,比方說gcc 2.6很難編譯得過kernel 3.x,這時候就要更新toolchain,如果沒有設定好設定案跟更新lib,恐怕功夫就不只是編譯這麼簡單
其次再考慮到要建立這些toolchain跟lib也要花上龐大的功夫,作者推薦了兩個前人努力的結果的套件,可以幫忙建立toolchain跟一些lib,分別是ptxdis跟buildroot
2012年2月21日 星期二
LCD之framebuffer(2)
在網路上找到了一些現成的程式拿來觀察,發現了一些有趣的現象
得到的結果是
很簡單的可以得知解析度為1366*768,每個pixel用32bits(4 bytes)來表示一切都還符合預期,但是最上面兩個我就有點疑問了,尤其是memory佔用的大小,4.0xMB,長度(line_length)如我之前猜測,可能因為1366這數字是16:9的解析度,所以實際上在處理的時候會用比較長的長度(1376)來表示,但是怎樣算術都是無法得到memory size的大小
不過我使用其他方式將1366*768*32塞入對應記憶體空間內的時候,卻是又可以填滿畫面,例如讓整個畫面呈現紅色或者藍色,表示其他記憶體區塊可能是沒有使用到或者被才切掉的,這部份我還搞不大懂
參考資料:
http://www.linuxgraphics.cn/graphics/fb_drawpoint.html
http://www.lslnet.com/linux/f/docs1/i48/big5328178.htm
得到的結果是
很簡單的可以得知解析度為1366*768,每個pixel用32bits(4 bytes)來表示一切都還符合預期,但是最上面兩個我就有點疑問了,尤其是memory佔用的大小,4.0xMB,長度(line_length)如我之前猜測,可能因為1366這數字是16:9的解析度,所以實際上在處理的時候會用比較長的長度(1376)來表示,但是怎樣算術都是無法得到memory size的大小
不過我使用其他方式將1366*768*32塞入對應記憶體空間內的時候,卻是又可以填滿畫面,例如讓整個畫面呈現紅色或者藍色,表示其他記憶體區塊可能是沒有使用到或者被才切掉的,這部份我還搞不大懂
參考資料:
http://www.linuxgraphics.cn/graphics/fb_drawpoint.html
http://www.lslnet.com/linux/f/docs1/i48/big5328178.htm
2012年2月20日 星期一
LCD之framebuffer(1)
有人把framebuffer當成driver,可是我稍微看了一下幾個系統,似乎framebuffer的個數並不一致(有的只有fb0,可是有的有fb1~fb3),還有看到有人講解framebuffer本身其實是一種抽象介面,我是蠻同意這樣的看法,不過我並未完全用程式檢驗任何一個平台完畢
其中有人提到下面有趣的實驗,但是我實驗並不如我所想得
對 framebuffer 操作:
dd if=/dev/fb0 of=fbfile
可以將 fb0 中的內容保存下來存到 fbfile 裡
如果顯示模式是 1024*768 的 8 位色
dd if=/dev/zero of=/dev/fb0 bs=1024 count=768
可以清空 framebuffer (螢幕全黑)
dd if=fbfile of=/dev/fb0
將 fbfile 內的資料寫回 framebuffer
其中有人提到下面有趣的實驗,但是我實驗並不如我所想得
對 framebuffer 操作:
dd if=/dev/fb0 of=fbfile
可以將 fb0 中的內容保存下來存到 fbfile 裡
如果顯示模式是 1024*768 的 8 位色
dd if=/dev/zero of=/dev/fb0 bs=1024 count=768
可以清空 framebuffer (螢幕全黑)
dd if=fbfile of=/dev/fb0
將 fbfile 內的資料寫回 framebuffer
在NB上實驗第二條,想要把整個螢幕填入乘黑色,我的解析度為1366x768,結果不論我填入fb=1366 count=768*3或者fb=1366 count=768*4(數值是乘法過後的數值,我這裡用乘法好理解),都無法整個填滿,另外就是其實不用急著把之前存下的資料蓋回去,只要拿著視窗"當抹布抹一抹"亂掉的區域就可以復原(少數位置抹不到,我覺得也不是很要緊,反正在實驗)
改天寫個C語言來把framebuffer內容倒出來看看,我猜測有一部份可能是1366還會被處理一些對齊的方式,可能被擴展到1440,或者其他解析度,其餘的被切除了
參考連結:
http://moto.debian.tw/viewtopic.php?t=11901&start=0&postdays=0&postorder=asc&highlight=
http://top12345tw.blogspot.com/2008/04/lcd-driver.html
參考連結:
http://moto.debian.tw/viewtopic.php?t=11901&start=0&postdays=0&postorder=asc&highlight=
http://top12345tw.blogspot.com/2008/04/lcd-driver.html
2012年2月19日 星期日
[好書推薦]建構嵌入式Linux系統(ch1-3)
這本書對於入門者,像我就是,真的是一本很不錯得書籍,我大致摘要一下書本內容
第一章
如果連linux都沒有摸過得人,真的必須要一看,不然大多是一些概念名詞介紹,可以跳過
第二章
開始分類介紹嵌入式系統幾種主要的模式,還有一些重要的元件
遷入系統大致上最重要的三個軟體bootloader、kernel、init,執行次序就是bootloader=>kernel=>init
作者提到開發環境主機(host)跟欲開發嵌入式系統(target)分三種
跟著介紹一個嵌入式系統的軟體介面,後面還有一些簡介各個軟硬體元件在開機流程中扮演的角色
第三章
著重硬體架構
第一章
如果連linux都沒有摸過得人,真的必須要一看,不然大多是一些概念名詞介紹,可以跳過
第二章
開始分類介紹嵌入式系統幾種主要的模式,還有一些重要的元件
遷入系統大致上最重要的三個軟體bootloader、kernel、init,執行次序就是bootloader=>kernel=>init
作者提到開發環境主機(host)跟欲開發嵌入式系統(target)分三種
- 最常見的是host跟target獨立,然後bootloader、kernel、init放在target上
- 其次,利用bootloader從網路或者其他地方載入kernel跟init,target上只有bootloader
- 最後,基本上很少看到,連開發環境都可以建立在target上面,也就不需要host就可以獨立開發
跟著介紹一個嵌入式系統的軟體介面,後面還有一些簡介各個軟硬體元件在開機流程中扮演的角色
第三章
著重硬體架構
- 各家CPU概略性介紹
- 各種BUS的概略性介紹
- 各種Memory Technology Device(MTD)以及storage device介紹
- 幾種常用的網路ethernet、wifi、bluetooth、IrDA
- 各種IO,像是mouse、lcd ... 等等
- 硬體的監控設備,watchdog概念簡介
2012年2月17日 星期五
架設tftp
這東西比ftp還不如,如他的名字一樣trivial ftp,照道理說,應該很快被淘汰的東西,但是還是屹立不搖主要是因為有其應用價值存在,如同RS232一樣
這個主要是用來把資料放到嵌入式系統內的,比方說beagleboard,如果每次要放一個檔案上去,就得把卡片拔下來,或者從新打包rootfs,真的是花費太多時間了
在PC/NB上面架設tftp server,跟著在beagleboard上面放上tftp client就可以了,假設兩者在同一個LAN底下,由同一台DHCP server配給IP
[PC/NB]
假設IP:192.168.1.100
apt-get install tftpd-hpa
然後檢查
/var/lib/tftpboot目錄在不在跟著確定權限
chown nobody:nogroup tftpboot/
chmod 777 tftpboot/
可以試著在該目錄下隨便新增一個文字檔案,待會可以測試
[beagleboard]
假設已經安裝了ubuntu,IP 192.168.1.103
apt-get insall tftp
然後tftp 192.168.1.103,跟著把之前的文字檔案抓下,然後檢查有沒有錯就大公告成了
參考資料:
http://suntosh.livejournal.com/26186.html
這個主要是用來把資料放到嵌入式系統內的,比方說beagleboard,如果每次要放一個檔案上去,就得把卡片拔下來,或者從新打包rootfs,真的是花費太多時間了
在PC/NB上面架設tftp server,跟著在beagleboard上面放上tftp client就可以了,假設兩者在同一個LAN底下,由同一台DHCP server配給IP
[PC/NB]
假設IP:192.168.1.100
apt-get install tftpd-hpa
然後檢查
/var/lib/tftpboot目錄在不在跟著確定權限
chown nobody:nogroup tftpboot/
chmod 777 tftpboot/
可以試著在該目錄下隨便新增一個文字檔案,待會可以測試
[beagleboard]
假設已經安裝了ubuntu,IP 192.168.1.103
apt-get insall tftp
然後tftp 192.168.1.103,跟著把之前的文字檔案抓下,然後檢查有沒有錯就大公告成了
參考資料:
http://suntosh.livejournal.com/26186.html
BeagleBoard XM刷上Ubuntu
主要參考這裡
http://elinux.org/BeagleBoardUbuntu#Maverick_10.10
過程出乎意料的簡單,但是也發現了寫入速度真的還不算快哩
先抓下需要的檔案
wget http://rcn-ee.net/deb/rootfs/oneiric/ubuntu-11.10-r5-minimal-armel.tar.xz
我利用了beagleboard給的microsd to sd的轉卡,所以必須要知道SD卡的位置(我的位置再/dev/sdb)
sudo ./setup_sdcard.sh --mmc /dev/sdb --uboot beagle_xm
http://elinux.org/BeagleBoardUbuntu#Maverick_10.10
過程出乎意料的簡單,但是也發現了寫入速度真的還不算快哩
先抓下需要的檔案
wget http://rcn-ee.net/deb/rootfs/oneiric/ubuntu-11.10-r5-minimal-armel.tar.xz
細心的話作個md5sum的檢查,然後解開壓縮檔
tar xJf ubuntu-11.10-r5-minimal-armel.tar.xz我利用了beagleboard給的microsd to sd的轉卡,所以必須要知道SD卡的位置(我的位置再/dev/sdb)
sudo ./setup_sdcard.sh --mmc /dev/sdb --uboot beagle_xm
接下來就是慢長得寫入過程,大概幾分鐘吧,完成之後就可以接上板子,直接開機測試囉
armel,這是啥?
因為老是在玩模擬的話感覺有點無聊,所以就買了個beagleboard mx,接著想要開始爬文怎樣把我想要的系統放上去,因為beagleboard xm本身硬體算是蠻強悍的,所以跟我本來預設的貴的多跟強悍的多,我本來預期有個64MB ram、cpu 400MHz就可以玩玩了,想不到是一個1G MHz的cpu加上一個800MHz的DSP。其實講了那麼多,我主要是看上了USB-OTG跟DVI output,很可惜沒有wifi的interface。
講了那麼多竟然還沒提到armel 囧,armel是我開始想要編譯自己的kernel跟rootfs的時候發現的,已經有人port ubuntu到這個平台上,而其中就有用到armel,就google一下,armel到底是何方神聖,跟著就得到了解釋
============
ABI
ABI,application binary interface,應用程式和作業系統的"low-level interface"。意思是,ABI有一套規則,如果遵循這個規則產生出的object,就能在相對應的系統上正確的運作。這個規則包括了data type的size,alignment,calling convention,function參數傳遞與回傳值,system call的調用等。很明顯的,這跟平台很有關係。比如說,不管用哪一個compiler,只要能生出符合ELF格式的binary,就可以在Linux上 跑,或呼叫其library。
而EABI,embedded-application binary interface則規定了embedded software program的規則。跟一般的作業系統的主要差異為: 應用程式可以使用privileged instruction,dynamic linking不是required,更簡潔的stack frame用以省下memory。
ARM EABI
而arm 的EABI有些改進,特別是floating point的運算。現在的arm應該都有實作FPA(Floating Point Accelerator),但如果compiler使用了FPU來做floting的運算,且CPU沒有FPU時,就會產生exception。其 panelty會是將指令轉為software的floating運算,當然系統也就變慢了。所以支援EABI的kernel與Cross- compiler,加上支援有實作FPA的arm,performance就會好很多。
armel
在lenny之後,就多了armel這個字,其與以前的arm差別就在於EABI這個interface。如前述,這個interface與硬體(CPU)的設計是很有關係的,對於arm不同的版本就會產生限制,因此EABI都常是建議在armv4t以後的版本。
============
ABI目前感覺好像還沒有很流行,聽說intel跳下去弄過一下,我想要弄到兩大巨頭arm跟intel同一格式恐怕有很大的難度(不是技術難度,應該是商業難度)
題外話,我很希望哪天嵌入式系統可以像是樂高積木玩具一樣,有眾多便宜的硬體元件可以整合再一起,像是現在一個beagleboard wifi的板子竟然要50USD以上,真的有貴到
beaglebone的價格就還不錯,很可惜的是硬體介面缺很大,勉強算是"堪用",我想除非很熟悉,不然就是特別的應用,不然beaglebone真的是只能"玩玩",不類似beagleboard xm可以玩很大XD
講了那麼多竟然還沒提到armel 囧,armel是我開始想要編譯自己的kernel跟rootfs的時候發現的,已經有人port ubuntu到這個平台上,而其中就有用到armel,就google一下,armel到底是何方神聖,跟著就得到了解釋
============
ABI
ABI,application binary interface,應用程式和作業系統的"low-level interface"。意思是,ABI有一套規則,如果遵循這個規則產生出的object,就能在相對應的系統上正確的運作。這個規則包括了data type的size,alignment,calling convention,function參數傳遞與回傳值,system call的調用等。很明顯的,這跟平台很有關係。比如說,不管用哪一個compiler,只要能生出符合ELF格式的binary,就可以在Linux上 跑,或呼叫其library。
而EABI,embedded-application binary interface則規定了embedded software program的規則。跟一般的作業系統的主要差異為: 應用程式可以使用privileged instruction,dynamic linking不是required,更簡潔的stack frame用以省下memory。
ARM EABI
而arm 的EABI有些改進,特別是floating point的運算。現在的arm應該都有實作FPA(Floating Point Accelerator),但如果compiler使用了FPU來做floting的運算,且CPU沒有FPU時,就會產生exception。其 panelty會是將指令轉為software的floating運算,當然系統也就變慢了。所以支援EABI的kernel與Cross- compiler,加上支援有實作FPA的arm,performance就會好很多。
armel
在lenny之後,就多了armel這個字,其與以前的arm差別就在於EABI這個interface。如前述,這個interface與硬體(CPU)的設計是很有關係的,對於arm不同的版本就會產生限制,因此EABI都常是建議在armv4t以後的版本。
============
ABI目前感覺好像還沒有很流行,聽說intel跳下去弄過一下,我想要弄到兩大巨頭arm跟intel同一格式恐怕有很大的難度(不是技術難度,應該是商業難度)
題外話,我很希望哪天嵌入式系統可以像是樂高積木玩具一樣,有眾多便宜的硬體元件可以整合再一起,像是現在一個beagleboard wifi的板子竟然要50USD以上,真的有貴到
beaglebone的價格就還不錯,很可惜的是硬體介面缺很大,勉強算是"堪用",我想除非很熟悉,不然就是特別的應用,不然beaglebone真的是只能"玩玩",不類似beagleboard xm可以玩很大XD
2012年2月15日 星期三
linux記憶體管理-VMA (Virtual Memory Address)
可執行檔案ELF被切割成很多section,對應不同的作用,其中包含symbol table,有的用來輔助記憶體定位,可是還沒有接觸到實際定位
先談談linux對記憶體的處理方式,linux將記憶體先分成兩部分,一部份是保留給kernel使用的1G,其餘是給user space,在32 bit的address line下是1G/3G(PAE case暫時不考慮),跟著這些使用者可以使用的記憶體再切成4k/2M大小區塊稱為page,這就是最基本單位,就類似某個國家硬幣最小就是4k/2M,沒有再小的單位了,雖然有2k,但也只是存款簿上的數字或者單純數字上。
核心就控制著如何把實體的記憶體配置給process,這中間有個單元叫做MMU,專職處理這樣的事情,跟著為了安全性找想,linux對process引入一個虛擬的區塊,一個可以使用的3G記憶體區塊稱Process Virtual Space,是一個邏輯上假的3G連續的記憶體,也就是實際上你需要的時候再配置空間給你,而且process的記憶體空間邏輯上雖然是連續,可是實際上未必。比方說process有個16k的陣列資料,在邏輯上他是連續的(0x00000000~00004000),但是經過MMU的手段之後,他可能佔據實際記憶體中的1,5,8,22這四個pages。這樣做有兩個好處,process可以專心在程式邏輯,不用管理位置如何應對,這部分委託給MMU,同時process因為接觸不到實體位置,他也不可能亂寫資料寫到別的process所用的位置。
ELF被loader載入成為程式(進入記憶體)的時候,他使用的 Process Virtual Space 的位置就是VMA (Virtual Memory Address),這時候.txt可能會被放在3G上面任何一個起始位置,跟著所有符號(如function name),根據這個位置做修正。那麼loader要如何將這些記憶體放置在記憶體中面臨到兩個問題,第一個section很多,載入很花時間,另外很多section很小,對於4k的page會浪費很多空間,比方說只有1k就要配置1 page,6k要配置2 pages。
所以loader引入了segment的觀念,segment將屬性相同的sections整理看成單一區塊,對應成相同的VMA區塊,例如有3個唯獨的sections大小分別為5, 3, 5 k,那麼視為VMA0,分配4個pages,另外可讀寫的2, 5, 7 k三個sections,視為VMA1分配4 pages,這樣一來就loader只要單存看屬性,簡單地分成6種左右的segment,不用處理一大堆的sections,也可以省下記憶體空間(如果是用section為單位配置分頁,一共要2+1+2跟1+2+2,總共10 pages)。下圖是elfread -l b.out的結果,可以看到整個elf被當成了6個segments,相同的segment有類似的屬性
上面是Process Virtual Space處理的方式,可是MMU在幫忙應對到實際記憶體的時候,更加的"吝嗇",記憶體是很寶貴的資源,屬於所有process共有,所以只好再請你們擠一下,將兩個segments對應到連續空間,就可以用9 pages擠進所有分配
所以記憶體的管理從ELF(sections)=>Process Virtual Space(loader)=>Physical(MMU)
先談談linux對記憶體的處理方式,linux將記憶體先分成兩部分,一部份是保留給kernel使用的1G,其餘是給user space,在32 bit的address line下是1G/3G(PAE case暫時不考慮),跟著這些使用者可以使用的記憶體再切成4k/2M大小區塊稱為page,這就是最基本單位,就類似某個國家硬幣最小就是4k/2M,沒有再小的單位了,雖然有2k,但也只是存款簿上的數字或者單純數字上。
核心就控制著如何把實體的記憶體配置給process,這中間有個單元叫做MMU,專職處理這樣的事情,跟著為了安全性找想,linux對process引入一個虛擬的區塊,一個可以使用的3G記憶體區塊稱Process Virtual Space,是一個邏輯上假的3G連續的記憶體,也就是實際上你需要的時候再配置空間給你,而且process的記憶體空間邏輯上雖然是連續,可是實際上未必。比方說process有個16k的陣列資料,在邏輯上他是連續的(0x00000000~00004000),但是經過MMU的手段之後,他可能佔據實際記憶體中的1,5,8,22這四個pages。這樣做有兩個好處,process可以專心在程式邏輯,不用管理位置如何應對,這部分委託給MMU,同時process因為接觸不到實體位置,他也不可能亂寫資料寫到別的process所用的位置。
ELF被loader載入成為程式(進入記憶體)的時候,他使用的 Process Virtual Space 的位置就是VMA (Virtual Memory Address),這時候.txt可能會被放在3G上面任何一個起始位置,跟著所有符號(如function name),根據這個位置做修正。那麼loader要如何將這些記憶體放置在記憶體中面臨到兩個問題,第一個section很多,載入很花時間,另外很多section很小,對於4k的page會浪費很多空間,比方說只有1k就要配置1 page,6k要配置2 pages。
所以loader引入了segment的觀念,segment將屬性相同的sections整理看成單一區塊,對應成相同的VMA區塊,例如有3個唯獨的sections大小分別為5, 3, 5 k,那麼視為VMA0,分配4個pages,另外可讀寫的2, 5, 7 k三個sections,視為VMA1分配4 pages,這樣一來就loader只要單存看屬性,簡單地分成6種左右的segment,不用處理一大堆的sections,也可以省下記憶體空間(如果是用section為單位配置分頁,一共要2+1+2跟1+2+2,總共10 pages)。下圖是elfread -l b.out的結果,可以看到整個elf被當成了6個segments,相同的segment有類似的屬性
上面是Process Virtual Space處理的方式,可是MMU在幫忙應對到實際記憶體的時候,更加的"吝嗇",記憶體是很寶貴的資源,屬於所有process共有,所以只好再請你們擠一下,將兩個segments對應到連續空間,就可以用9 pages擠進所有分配
所以記憶體的管理從ELF(sections)=>Process Virtual Space(loader)=>Physical(MMU)
2012年2月14日 星期二
USB名詞解釋
1. usb不支援peer to peer的傳輸,因此必須是master/slave
2. 隨身碟插入電腦,host為電腦,隨身碟為device (通常pc都是host)
3. 在沒有電腦的情況下,兩個devices要傳輸,仍然必須基於master/slave的架構。因此就有了OTG這個技術,使得device可藉由 protocol ( HNPHost negotiation protocol)的溝通決定誰是host/device。例如,camera直接輸出到printer,則camera須為host,printer需 為device。因此camera必須支援OTG傳輸。兩台mp3 player的情況也類似,兩台支援OTG的mp3連在一起,一樣會有一個host一個device。
4. 通常pc上不會有OTG,而都是host,但公板上可能會有一個OTG,這是為了在download mode的需要。
參考:
http://kezeodsnx.pixnet.net/blog/post/27973723-usb-host-device-otg
有趣的ELF觀察
如果要寫個印出"Hello World!"字串的程式,大概很多人都會,簡單的用printf就可以達成,不消幾行codes,那麼大小呢?
tinytest.c內容如下
編譯指令如下
gcc -c -fno-builtin tinytest.c
ld -static -e mymain -o tinytest tinytest.o
可以注意到整個程式沒有main(),我們使用ld指令來指定entry function,exit()這個function其實可以從include/unistd.h內拷貝出來,對於組合語言有興趣的可以參考參考資料的連結
簡單從sections觀察一下
也是依序從a.out, b.out, tinytest,可以看到a.out sections最多,然後b.out的一些.data等最大,顯然引入很多"不必要"的資訊,最後tinytest相對section跟section size小很多,如果有興趣可以再去研究symbol table可以得到更多的答案,但就此打住吧。
我想有人會想問,還可以更小嗎?答案是可以的,至少把.comment這個section去除,就可以省下不少空間,也可以把.strtab ...等的section除去,還可以進步縮小,但這些大多只有在小程式有用,或者在極度記憶體限制下才來的必須。畢竟現在開發時間、移植性以及電腦速度大多遠大於我們對於performance以及optimization的過度追求,但也不表示程式能放棄品質於不顧,隨意寫出O(n^k)這樣的code(k>=2)
參考資料
http://c9s.blogspot.com/2008/06/linux-gnu-as-1.html
- a.out是使用一般編譯(gcc test.c這樣),因為library放在外面,所以大小大約7kB
- b.out跟著使用-static引入所有lib,可怕的大啊,變成了570kB左右的大小,請比較一下busybox,就可以感受到busybox的威力,小小印個"Hello World!"就要這樣的size,busybox可是支援數十道指令阿!!
- 最後是tinytest.c所編譯出來的,他刪除了呼叫glibc.lib的必要性,並且使用了組合語言,整體大小就小了很多
tinytest.c內容如下
編譯指令如下
gcc -c -fno-builtin tinytest.c
ld -static -e mymain -o tinytest tinytest.o
可以注意到整個程式沒有main(),我們使用ld指令來指定entry function,exit()這個function其實可以從include/unistd.h內拷貝出來,對於組合語言有興趣的可以參考參考資料的連結
簡單從sections觀察一下
也是依序從a.out, b.out, tinytest,可以看到a.out sections最多,然後b.out的一些.data等最大,顯然引入很多"不必要"的資訊,最後tinytest相對section跟section size小很多,如果有興趣可以再去研究symbol table可以得到更多的答案,但就此打住吧。
我想有人會想問,還可以更小嗎?答案是可以的,至少把.comment這個section去除,就可以省下不少空間,也可以把.strtab ...等的section除去,還可以進步縮小,但這些大多只有在小程式有用,或者在極度記憶體限制下才來的必須。畢竟現在開發時間、移植性以及電腦速度大多遠大於我們對於performance以及optimization的過度追求,但也不表示程式能放棄品質於不顧,隨意寫出O(n^k)這樣的code(k>=2)
參考資料
http://c9s.blogspot.com/2008/06/linux-gnu-as-1.html
elf中的header跟section header
從wikipedia借來一張圖
整個elf檔案包裝大致上類似這樣,如果稍微寫過一些解析圖片或者特定檔案格式的人都知道,file header就是用來儲存跟這個檔案有關的資訊,比方說jpg裡面就會有些圖片大小,壓縮方式跟比例等等的資訊。上圖片中ELF header存有的資訊可以用elfread -h來觀察
很多資訊網路上可以找到,在/usr/include/elf.h上也有定義相對應的結構(c struct),在此就不說了,先提到program header table是optional,在圖片中248 (bytes into files)上,可以看到它是0 bytes。
接著用readelf -S來讀取每個section的資訊
這部分的資料可以從header section table裡面取得,有興趣一樣可以在/usr/include/elf.h上找到對應的結構,其中也包含了各種重要的屬性
參考資料:
http://blog.chinaunix.net/space.php?uid=20547746&do=blog&id=1647100
http://beye.sourceforge.net/en/beye.html
整個elf檔案包裝大致上類似這樣,如果稍微寫過一些解析圖片或者特定檔案格式的人都知道,file header就是用來儲存跟這個檔案有關的資訊,比方說jpg裡面就會有些圖片大小,壓縮方式跟比例等等的資訊。上圖片中ELF header存有的資訊可以用elfread -h來觀察
很多資訊網路上可以找到,在/usr/include/elf.h上也有定義相對應的結構(c struct),在此就不說了,先提到program header table是optional,在圖片中248 (bytes into files)上,可以看到它是0 bytes。
- 第一個紅色框框得知section header table相對於檔案的位移(offset),方便直接移動到那處讀取每個section的section header,因為section header是包含許多重要屬性,因為知道有那些section對ld是很重要的
- 第二個紅色框框可以看到這個檔案包含了多少個sectioin
接著用readelf -S來讀取每個section的資訊
參考資料:
http://blog.chinaunix.net/space.php?uid=20547746&do=blog&id=1647100
http://beye.sourceforge.net/en/beye.html
用objdump指令觀察ELF檔案
objdump是個用來觀察object code的好幫手,雖然readelf可以取代他大部分的工作,首先先有個code
很簡單的code,基本學過C望文生義
程式大致上可以分為兩部分,執行的資料跟處理資料的程式碼。obdump參數h表示要列出sections,object code由許多sections所組成,其中.txt這個section是處理資料的程式碼,.data與.bss是資料的section,還有其他給ld用的區段 size指令可以看出各個section的大小
s這個參數表示要使用16進位表示法顯示出object code中的各個區段,可以看到一些read only的資料室蠻明顯的,但是.txt基本上不是人看得,最後可以觀察到.comment這個section是可以省略掉的資訊
d這個參數讓object code可以反組譯成assemble code,方便我們觀察程式,有些時候只能拿到library的時候,就只能從這裡挖到一些資料了,雖然可讀性沒有C好,但是至少比binary code強
最後x參數可以所是列出了所有資訊,不只包含了h這個參數,還列出了許多symbol table跟一些需要重新定位label資訊
這裡觀察到兩個有趣的地方,兩個變數被寫進了不同的區段,有初始化的m被寫入了.data內,沒有的則被寫入了.bss,printf不見了,其實被取代成了puts,這是compiler優化的結果,puts在處理只有字串的資訊比printf有效率,UND表示尚未定義在這個object code中
參考資料:
http://www.jollen.org/EmbeddedLinux/Executable_Linking_Format.html
http://www.jollen.org/blog/2007/03/elf_program_loading_1_segment.html
http://www.jollen.org/blog/2007/03/elf_program_loading_2_pht.html
http://www.study-area.org/cyril/opentools/opentools/x909.html
很簡單的code,基本學過C望文生義
程式大致上可以分為兩部分,執行的資料跟處理資料的程式碼。obdump參數h表示要列出sections,object code由許多sections所組成,其中.txt這個section是處理資料的程式碼,.data與.bss是資料的section,還有其他給ld用的區段 size指令可以看出各個section的大小
s這個參數表示要使用16進位表示法顯示出object code中的各個區段,可以看到一些read only的資料室蠻明顯的,但是.txt基本上不是人看得,最後可以觀察到.comment這個section是可以省略掉的資訊
d這個參數讓object code可以反組譯成assemble code,方便我們觀察程式,有些時候只能拿到library的時候,就只能從這裡挖到一些資料了,雖然可讀性沒有C好,但是至少比binary code強
最後x參數可以所是列出了所有資訊,不只包含了h這個參數,還列出了許多symbol table跟一些需要重新定位label資訊
這裡觀察到兩個有趣的地方,兩個變數被寫進了不同的區段,有初始化的m被寫入了.data內,沒有的則被寫入了.bss,printf不見了,其實被取代成了puts,這是compiler優化的結果,puts在處理只有字串的資訊比printf有效率,UND表示尚未定義在這個object code中
參考資料:
http://www.jollen.org/EmbeddedLinux/Executable_Linking_Format.html
http://www.jollen.org/blog/2007/03/elf_program_loading_1_segment.html
http://www.jollen.org/blog/2007/03/elf_program_loading_2_pht.html
http://www.study-area.org/cyril/opentools/opentools/x909.html
2012年2月12日 星期日
用qemu觀察interrupt以及製作system call
網路上的資料教我們怎樣做到這兩件事情,還頗為有趣,首先是觀察interrupt
interrupt事實上在硬體設備上是有限的,所以大多時候是必須要分享的,除此之外,還有目前更複雜的多核心中斷處理(想想,一個中斷發生,但是有多個核心,誰該去處理這件事情?)。因為共享,所以有時候在設備有接上主機的時候,在跟系統要求中斷,不要用的時候必須歸還,或者說暫時切換,這樣才讓系統在有限的資源內可以為多個硬體的中斷做服務
有了以上的概念,跟著就是直接切入code,在上面印出資訊,至kernel source目錄,進入drivers/mmc/mmci.c這個source code,這是一個SD Card Host Controller driver,在
ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);之前加上printk,因為已經在kernel mode就不要再用printf,printf是給user mode用的
printk("\n***************Hello Interrupt*******************\n");
重新編譯kernel
make CROSS_COMPILE=arm-linux- ARCH=arm
接著把產生出來的kernel image放到之前busybox目錄,跟著執行,這些中斷就註冊上去,所以在開機訊息上可以看到,如下圖(我拼錯字了~囧,不管他)
===============
interrupt事實上在硬體設備上是有限的,所以大多時候是必須要分享的,除此之外,還有目前更複雜的多核心中斷處理(想想,一個中斷發生,但是有多個核心,誰該去處理這件事情?)。因為共享,所以有時候在設備有接上主機的時候,在跟系統要求中斷,不要用的時候必須歸還,或者說暫時切換,這樣才讓系統在有限的資源內可以為多個硬體的中斷做服務
- request_irq() :向系統註冊使用某一個 interrupt ,並指定負責處理該 interrupt 的 ISR 。
- free_irq() :告知系統釋放特定的 interrupt 。
有了以上的概念,跟著就是直接切入code,在上面印出資訊,至kernel source目錄,進入drivers/mmc/mmci.c這個source code,這是一個SD Card Host Controller driver,在
ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);之前加上printk,因為已經在kernel mode就不要再用printf,printf是給user mode用的
printk("\n***************Hello Interrupt*******************\n");
重新編譯kernel
make CROSS_COMPILE=arm-linux- ARCH=arm
接著把產生出來的kernel image放到之前busybox目錄,跟著執行,這些中斷就註冊上去,所以在開機訊息上可以看到,如下圖(我拼錯字了~囧,不管他)
===============
接下來自行建立一個system call,system call是系統提供給一般程式開發者作為有限存取kernel功能的一個介面,可以想成開發kernel的人提供給programmer的API,必須要做的事情有五件
- 製作system call的功能,大致上一般程式設計差不多,不過是kernel mode
- 註冊 system call 的名字
- 定義新 system call 的代碼
- 調整 Makefile ,使 systam call 被包含在 kernel 中
- 增加 system call 的 header file ,讓 user program 能夠 include
第二、三件事情對於一般程式設計師比較陌生,但是如果有看過system programming的書籍,大致上會提到過,底下要注意,大多目錄是相對於kernel source code目錄,在定義function name、system call number有一些prefix words要注意
第一步在arch/arm/kernel下建立mytestcall.c,內容是
#include <linux/linkage.h>
#include <linux/kernel.h>
asmlinkage void sys_mytestcall(){
static int count = 0;
printk("mytestcall has been called for %d time(s)\n", ++count);
}
第四步驟,在arch/arm/kernel/Makefile裡面,sys_mytestcall.o加到target後面,讓他可以被編譯,這時候就可以重新編譯kernel,指令跟前面一樣
看到用了asmlinkage的修飾子跟sys_這個prefix
第二步在arch/arm/kernel/calls.S下將創立的system call的名稱做註冊上去在
CALL(sys_set_mempolicy)
後面加上
CALL(sys_mytestcall)
後面加上
CALL(sys_mytestcall)
第三步驟,號碼註冊上去,直接就取前面那個最小的+1即可,前面最小的是,include/asm-arm/unistd.h裡面,最小的是底下
#define __NR_set_mempolicy (__NR_SYSCALL_BASE+321)第四步驟,在arch/arm/kernel/Makefile裡面,sys_mytestcall.o加到target後面,讓他可以被編譯,這時候就可以重新編譯kernel,指令跟前面一樣
make CROSS_COMPILE=arm-linux- ARCH=arm
第五件事情,是為了一般programmer提供header file,就如大家寫lib,總是要提供head file讓人家include,才可以做symbol以及link,命名include/linux/mytestcall.h
#include <linux/unistd.h>
#define __NR_mysyscall (__NR_SYSCALL_BASE+322)
#define mytestcall(void) syscall(__NR_mytestcall);
===============
===============
以上事情完成就可以開始寫測試程式了,我是直接就在linux kernel source code目錄底下建立一個名為testmycall.c,內容很簡單
#include "linux/mysyscall.h"
int main(){
mytestcall();
return 0;
}
跟著編譯他,指令如下
arm-linux-gcc -I ./include -static testmycall.c -o testmycall
完成後把新的kernel放到busybox目錄下,把這個testmycall拷貝到busybox內的_install目錄底下,然後重新用cpio命令打包_install目錄成root image,用qemu開機,跟著直行就可以看到成果了
這裡可以看到有趣的現象,一般的程式在結束的時候,static variable也跟著被釋放掉,再次執行該程式就重新來過,但是這裡system call是屬於kernel mode,他生命週期一般跟第一次載入記憶體後,就待到觀及,所以再次呼叫的時候,裡面的static變數還是繼續加1變成了2
一般而言儘量避免使用這種static用法,因為system call有點類似global變數,如果使用了這樣的用法,很容易造成race condition的情形。
一般而言儘量避免使用這種static用法,因為system call有點類似global變數,如果使用了這樣的用法,很容易造成race condition的情形。
吃盡苦頭編譯busybox(2)
了解了一些開機流程之後,大概知道要補足那些東西了,進入_install目錄,開始建立缺乏的目錄跟檔案
mkdir etc dev tmp proc sys
跟著在etc底下在建立init.d,新增檔案etc/init.d/rcS,內容是
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/mdev -s
/bin/sh
把rcS加上可執行權限
chmod +x rcS
接著把busybox連結到_install\init,你可以看到_install底下有linuxrc,這是2.4的名稱,在2.6之後使用init
ln -s bin/busybox init
接下來進入/dev底下建立裝置,必須用root權限
mknod console c 5 1
mkdir etc dev tmp proc sys
跟著在etc底下在建立init.d,新增檔案etc/init.d/rcS,內容是
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/mdev -s
/bin/sh
把rcS加上可執行權限
chmod +x rcS
接著把busybox連結到_install\init,你可以看到_install底下有linuxrc,這是2.4的名稱,在2.6之後使用init
ln -s bin/busybox init
接下來進入/dev底下建立裝置,必須用root權限
mknod console c 5 1
mknod null c 1 3
還有補上etc/inittab,內容是
::sysinit:/etc/init.d/rcS
這樣就都完成了,重新打包image吧
find . | cpio -o --format=newc > ../rootfs.img
回到busybox目錄,使用qemu測試
qemu-system-arm -kernel zImage -initrd rootfs.img
後話:這裡我都沒有把image做壓縮的動作,因為測試方便,如果要放上去,壓縮一下可以節省空間,另外裡面還有個小小的錯誤,我想以後再說吧:P
2012年2月11日 星期六
開機流程
bootloader在載入kernel之後,kernel會連帶載入initrd,也就是busybox打包起來的image檔案,理由在於kernel為了精簡,他只處哩cpu排程、記憶體管理...等重要功能,並沒有root file system,且沒有drivers的部分,driver的捨棄是因為全部包起來太過龐大了,所以把這部分委託給另外一個initrd去建立一個虛擬的root file system,並且載入記憶體當作root file system,這樣就可以順利把drivers載入了
bootloader把initrd載入記憶體後,kernel就排程,並且安排initrd中的一個init的行程去工作,init主要就是呼叫一個scripts(/etc/init.d/rcS),把其他工作完成,然後接手許多工作,底下是script大致紹的工作
可以用之前的busybox觀察,因為只有建立一個shell,先用ls觀察,發現目錄少的可憐,也因為沒執行script,所以上面列出的功能都沒有做,當使用ps指令的時候,系統會抱怨找不到/proc,/proc的意思是process file system,原意是用來儲存process資訊的地方,使用手動指令把掛載起來
mkdir /proc
mount -t proc none /proc
bootloader把initrd載入記憶體後,kernel就排程,並且安排initrd中的一個init的行程去工作,init主要就是呼叫一個scripts(/etc/init.d/rcS),把其他工作完成,然後接手許多工作,底下是script大致紹的工作
- 建立/proc跟/sys
- 建立必要的/dev裝置,可以看到有許多mknod指令,乃至於hotplug
- 載入drivers,會看到一堆insmod xxx.ko
- 真的引入linux的file system
- 一些類似網路設定等等也可以寫死在這裡
可以用之前的busybox觀察,因為只有建立一個shell,先用ls觀察,發現目錄少的可憐,也因為沒執行script,所以上面列出的功能都沒有做,當使用ps指令的時候,系統會抱怨找不到/proc,/proc的意思是process file system,原意是用來儲存process資訊的地方,使用手動指令把掛載起來
mkdir /proc
mount -t proc none /proc
這時候在執行ps,就可以看到畫面了,kernel就不會再抱怨找不到這個file system
其實使用/proc的人都知道,裡面其實還可以找到很多裝置的資訊,乃至於其他有用資訊,本來單純的process file system也變複雜
為此Linus決定在2.6引入另外一個/sys目錄,意思是system file system來儲存其他有用資訊,這個目錄會用來重新整理一些有用的資訊如/sys/block用來儲存區塊裝置,如硬碟的資料,/sys/bus則用來找尋硬體裝置,使得hotplug功能更容易實踐。
所以掛載/sys也是必須的
mkdir /sys
mount -t sys none /sys
其實還缺少了不少東西
/dev這個重要目錄,它是用來儲存裝置資訊了
/etc這個設定檔案的目錄,且重要的/etc/init.d/rcS這個script檔案就是放在這裡啊
/etc/inittab這個filesystme的分割紀錄檔案
/tmp暫存目錄
吃盡苦頭編譯busybox(1)
小小的設定錯誤跟失誤讓人吃進苦頭orz
首先下載去下載busybox,真著把tarball解開後,進入目錄
make ARCH=arm CROSS_COMPILE=arm-linux- defconfig
首先下載去下載busybox,真著把tarball解開後,進入目錄
make ARCH=arm CROSS_COMPILE=arm-linux- defconfig
這指令會載入default config,跟著自行定義需要的config
make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig
我在menuconfig內只有多選了一個選項就是,把busybox打包成static library,這樣以後就不用考被一堆library了
[插曲]
因為兩個失誤害我編譯了好幾個小時跟做了好多測試
1. 把CROSS_COMPILE打成CROSS_COMPILER,make完全沒有抱怨,只是把它當成額外的巨集定義orz,測試了好久,qemu都跟我說init不能執行,因為x86的指令不能跑在arm上面阿~囧,隔了幾個小時才注意到
2. 在建立toolchain的時候沒有編譯large file support,這個會造成錯誤,我只好從新編譯toolchain,又花了好久的時間:P
跟著下
make ARCH=arm CROSS_COMPILE=arm-linux- install
這樣開始編譯,並且編譯完畢的檔案會被放到一個名為_install的目錄底下,可以注意到,指令只有差了後面參數的defconfig=>menuconfig=>install。另外一個有意思的觀察可以進入_install,然後下ls -la,可以發現所有的指令都是link到busybox這個檔案
接下進入_install目錄來打包一下root file system,用qemu測試能不能用,免得犯了跟我一樣的錯誤
find . | cpio -o --format=newc > ../rootfs.img
然後回到上一層目錄,把上次拷貝的zImage那個kernel拷貝過來,執行
qemu-system-arm -kernel zImage -initrd rootfs.img -append "root=/dev/ram rdinit=/bin/sh"
最後參數的意思是把init用/bin/sh取代,所以開機就會去執行/bin/sh,這時候應該會看到類似下面的畫面
參考資料
http://balau82.wordpress.com/2010/03/27/busybox-for-arm-on-qemu/
http://balau82.wordpress.com/2010/03/27/busybox-for-arm-on-qemu/
2012年2月9日 星期四
開發工具
這裡簡單紀錄一下我所看到的開發工具
- gdb : 不用多說了,這根本是一定會提到的
- strace : 用來追蹤system call
- ltrace : 用來追蹤library的function call
- mtrace : 追蹤記憶體配置,如malloc()、realloc()、free()
- dmalloc : 比上面更強大,但是更複雜的工具
- readelf : 了解執行檔案組成,跟裡面更種section的部分
- objdump : 跟readelf有些功能從跌,但是有個好處是可以反組譯object code
- objcopy : 格式或者轉換一個binary object code,如塞入一個section到elf
- strip : 刪除binary內的symbol跟debug information
- ldd : 顯示lib相關性
- nm : 顯示object code的symbols,如function call names等
編譯kernel for qemu
抓取linux kernel的 tar ball
wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.18.tar.gz
解開後
make menuconfig ARCH=arm
載入這個設定檔案
wget http://opencsl.openfoundry.org/setting/linux-2.6.18.config
跟著開始編譯
make ARCH=arm CROSS_COMPILE=arm-linux-
跟著又是漫長的等待,編譯完成後找到zImage這個檔案,我是在解開tar ball的目錄下的arch/arm/boot目錄下找到他,切換到該目錄,然後將之前的arm-test-0.2的arm_root.img拷貝過來,執行
qemu-system-arm -kernel zImage -initrd arm_root.img
如果可以看到qemu畫面表示沒有問題,可以用uname -a對比之前的kernel版本,一個是2.6.17一個是2.6.18,有差異的建立toolchain
先到http://buildroot.uclibc.org/這裡下載buildroot,用來建立toolchain,toolchain大致上可以看成一種編譯環境,讓人可以建立kernel等等必須元件的工具
他其實是一個make file set,不過這個make file set會到網路上抓取必須要的檔案來編譯,所以大多數檔案不在抓下來的tar ball內,所以過程會蠻久的,使用前先安裝必須的套件
apt-get install bison flex gettext patch build-essential texinfo ncurses
他其實是一個make file set,不過這個make file set會到網路上抓取必須要的檔案來編譯,所以大多數檔案不在抓下來的tar ball內,所以過程會蠻久的,使用前先安裝必須的套件
apt-get install bison flex gettext patch build-essential texinfo ncurses
跟著使用make menuconfig
- 修改Target Architechure為arm
- 取消製作root system,把Target filesystem options中的ext2 root filesystem取消
- 不要做kernel,Kernel type選為none
- Tool Chain內選擇開啟RPC,這是為了以後的NFS
設定檔案儲存後就可以開始編譯了make
跟著找到arm-linux-的這群檔案,我編譯出來的檔案被放到了tar ball目錄下的output/host/usr/bin內,可以使用find指令,跟著把他加入PATH
最後試試看arm-linux-gcc -v,可以看到版本
qemu初步
我使用在虛擬機器上架設ubuntu 10.10,系統是沿用之前安裝好skyeye,所以一些關於build-essential的套件也已經安裝完畢了
然後再安裝apt-get install qemu-system(根據說明,這就是完整的qemu系統,應該意思是有各家平台的CPU模擬)
跟著到qemu網站下載arm-test-0.2的測試版image回來,並且解開後切換到他的該目錄檔案底下,測試的方式很簡單,打開他的README file就可以看到兩行指令,我是使用第一行
qemu-system-arm -kernel zImage.integrator -initrd arm_root.img
結果就跟這張圖片一樣,感覺還蠻有趣的,看來是用了busybox
P.S. 題外話,要不是已經可以裝gnome-shell,我是不想用Ubuntu那個Unity爛介面的
然後再安裝apt-get install qemu-system(根據說明,這就是完整的qemu系統,應該意思是有各家平台的CPU模擬)
跟著到qemu網站下載arm-test-0.2的測試版image回來,並且解開後切換到他的該目錄檔案底下,測試的方式很簡單,打開他的README file就可以看到兩行指令,我是使用第一行
qemu-system-arm -kernel zImage.integrator -initrd arm_root.img
結果就跟這張圖片一樣,感覺還蠻有趣的,看來是用了busybox
P.S. 題外話,要不是已經可以裝gnome-shell,我是不想用Ubuntu那個Unity爛介面的
從原始碼到可執行檔
編譯原始碼,最原始的指令就是gcc helloworld.c -o helloworld,其實中間跳過了很多的過程,這中間的過程隱藏了許多過去前人所累積的技術能量,其實過程大致上如下,並不是一下子就從source code變成binary code
先掠過preprocessing的部分(它基本上只比copy/paste複雜一些,是大量的取代動作),單單是從source code產生.s就需要很大的技術,底下可以看到流程
線上的字體,可以看成一種軟體元件,並不一定會在檔案系統上留下檔案,在System Software的書籍上有比較詳細的介紹。先用Scanner將source code切成一堆單位/token,這些東西表示該程式語言內接受的元件,比方說有++i,被分解成++跟i,接著建構Syntax Tree,透過事先定義的grammar,將statement建構出來,有時會牽涉到類似先乘除後加減的概念,跟著將語意辨識出來,就是賦予型態等等意涵,在原始碼上i就是i,除非你找到int i的宣告,不然他並不會在code上面有意義,但是電腦是很死板,必須有人把每個i標註上他的型態,如整數或者物件等等意涵。接著再把該tree轉成一些p-code,對應到了組合語言,中間當然會有一些最佳化的議題,在此略過不談
在討論linking這動作之前,先理解object code,object code其實很豐富,可以用file指令來觀察檔案屬性
可以看到都是ELF (Executable Linkable Format)格式,object稱為是一個relocatable的,bash是一個可執行檔,so則是share object
- Preprocessing過程先把大部分的#include之類的編譯指令拿掉,直接將一些function code變成一個text file,比方說printf()這個function會被加入一個暫時的文字檔案(.i)中,把註解拿掉,因為他對編譯程式沒有意義,註解是給人看得,不是給機器看的,種種目的就是為了純化資料到電腦必須要處理的
- Compilation:將source跟著就是轉成組合語言,這是平台相關性的code,如果是arm CPU,就轉成arm的組合語言指令,如果是x86就轉成x86,這時候已經進入平台相關的議題了
- Assembly: 將組合語言轉為object code,object跟作業系統的平台很有相關,比方說ELF(Unix/Linux)或者PE(M$)
- Linking: 則是將一些object code做relocation等等動作,還有引入library
先掠過preprocessing的部分(它基本上只比copy/paste複雜一些,是大量的取代動作),單單是從source code產生.s就需要很大的技術,底下可以看到流程
線上的字體,可以看成一種軟體元件,並不一定會在檔案系統上留下檔案,在System Software的書籍上有比較詳細的介紹。先用Scanner將source code切成一堆單位/token,這些東西表示該程式語言內接受的元件,比方說有++i,被分解成++跟i,接著建構Syntax Tree,透過事先定義的grammar,將statement建構出來,有時會牽涉到類似先乘除後加減的概念,跟著將語意辨識出來,就是賦予型態等等意涵,在原始碼上i就是i,除非你找到int i的宣告,不然他並不會在code上面有意義,但是電腦是很死板,必須有人把每個i標註上他的型態,如整數或者物件等等意涵。接著再把該tree轉成一些p-code,對應到了組合語言,中間當然會有一些最佳化的議題,在此略過不談
在討論linking這動作之前,先理解object code,object code其實很豐富,可以用file指令來觀察檔案屬性
可以看到都是ELF (Executable Linkable Format)格式,object稱為是一個relocatable的,bash是一個可執行檔,so則是share object
2012年2月8日 星期三
有看無懂的CFS Scheduler
列出三個參考
http://www.ibm.com/developerworks/cn/linux/l-cn-scheduler/index.html
http://www.ibm.com/developerworks/cn/linux/l-completely-fair-scheduler/
http://www.ibm.com/developerworks/cn/linux/l-cn-scheduler/index.html
老實說,這個scheduler主要是使用運行時間均勻非配,而非過去的整體統計計算之後再決定,CFS只要找出最前面那個需要執行的task,執行過後再把它放回紅黑數內就可以。但是我不懂的是判斷的準則是?執行時間比較少的?如果說有個無所事事的process也要頻頻把它挑出來執行嗎?從資料結構的角度我可以同意他比過去方式有效率,但是是否會讓process執行更有效率?我還沒看懂
http://www.ibm.com/developerworks/cn/linux/l-cn-scheduler/index.html
http://www.ibm.com/developerworks/cn/linux/l-completely-fair-scheduler/
http://www.ibm.com/developerworks/cn/linux/l-cn-scheduler/index.html
老實說,這個scheduler主要是使用運行時間均勻非配,而非過去的整體統計計算之後再決定,CFS只要找出最前面那個需要執行的task,執行過後再把它放回紅黑數內就可以。但是我不懂的是判斷的準則是?執行時間比較少的?如果說有個無所事事的process也要頻頻把它挑出來執行嗎?從資料結構的角度我可以同意他比過去方式有效率,但是是否會讓process執行更有效率?我還沒看懂
名詞解釋boot loader、kernel、root file system
我野人獻曝一下,雖然對嵌入式系統還不是很了解,但是先記錄一下我所看到的東西,因為網路上實在太多名詞,讓人有點頭昏眼花
這些東西都是牽涉到整個嵌入式系統從硬體開始啟動到載入OS的過程,我想可以參考鳥哥的網站的內容,流程大致上應該是
BIOS=>boot loader=>kernel=>root file system,底下是鳥哥畫的示意圖
底下是節錄鳥哥的開機流程
=======
當我們藉由 boot loader 的管理而開始讀取核心檔案後,接下來, Linux 就會將核心解壓縮到主記憶體當中,
並且利用核心的功能,開始測試與驅動各個周邊裝置,包括儲存裝置、CPU、網路卡、音效卡等等。
此時 Linux 核心會以自己的功能重新偵測一次硬體,而不一定會使用 BIOS
偵測到的硬體資訊喔!也就是說,核心此時才開始接管 BIOS 後的工作了。
那麼核心檔案在哪裡啊?一般來說,他會被放置到 /boot 裡面,並且取名為 /boot/vmlinuz 才對!
從上表我們也可以知道此版本的 Linux 核心為 2.6.18-92.el5 這個版本!為了硬體開發商與其他核心功能開發者的便利,
因此 Linux 核心是可以透過動態載入核心模組的 (就請想成驅動程式即可),這些核心模組就放置在 /lib/modules/ 目錄內。
由於模組放置到磁碟根目錄內 (要記得 /lib 不可以與 / 分別放在不同的 partition !),
因此在開機的過程中核心必須要掛載根目錄,這樣才能夠讀取核心模組提供載入驅動程式的功能。
而且為了擔心影響到磁碟內的檔案系統,因此開機過程中根目錄是以唯讀的方式來掛載的喔。
一般來說,非必要的功能且可以編譯成為模組的核心功能,目前的 Linux distributions 都會將他編譯成為模組。 因此 USB, SATA, SCSI... 等磁碟裝置的驅動程式通常都是以模組的方式來存在的。 現在來思考一種情況,假設你的 linux 是安裝在 SATA 磁碟上面的,你可以透過 BIOS 的 INT 13 取得 boot loader 與 kernel 檔案來開機,然後 kernel 會開始接管系統並且偵測硬體及嘗試掛載根目錄來取得額外的驅動程式。
問題是,核心根本不認識 SATA 磁碟,所以需要載入 SATA 磁碟的驅動程式, 否則根本就無法掛載根目錄。但是 SATA 的驅動程式在 /lib/modules 內,你根本無法掛載根目錄又怎麼讀取到 /lib/modules/ 內的驅動程式?是吧!非常的兩難吧!在這個情況之下,你的 Linux 是無法順利開機的! 那怎辦?沒關係,我們可以透過虛擬檔案系統來處理這個問題。
虛擬檔案系統 (Initial RAM Disk) 一般使用的檔名為 /boot/initrd ,這個檔案的特色是,他也能夠透過 boot loader 來載入到記憶體中, 然後這個檔案會被解壓縮並且在記憶體當中模擬成一個根目錄, 且此模擬在記憶體當中的檔案系統能夠提供一支可執行的程式,透過該程式來載入開機過程中所最需要的核心模組, 通常這些模組就是 USB, RAID, LVM, SCSI 等檔案系統與磁碟介面的驅動程式啦!等載入完成後, 會幫助核心重新呼叫 /sbin/init 來開始後續的正常開機流程。
如上圖所示,boot loader 可以載入 kernel 與 initrd ,然後在記憶體中讓 initrd 解壓縮成為根目錄, kernel 就能夠藉此載入適當的驅動程式,最終釋放虛擬檔案系統,並掛載實際的根目錄檔案系統, 就能夠開始後續的正常開機流程。更詳細的 initrd 說明,你可以自行使用 man initrd 去查閱看看。 底下讓我們來瞭解一下 CentOS 5.x 的 initrd 檔案內容有什麼吧! ^_^
=======
因為一般PC上的linux實在太大了,所以有很多精簡化的東西,也有許多因為某些目的發展出來的專案,嵌入式系統比較少從無到有的,所以大多講究porting
boot loader : u boot
kernel : uClinux
root file system : uClinux、busybox
最後是兩篇人家網路上對新手的建議
http://phorum.study-area.org/index.php?topic=58481.0
http://jslinux.pixnet.net/blog/post/12065743-%E5%A6%82%E4%BD%95%E9%80%B2%E5%85%A5linux%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%B3%BB%E7%B5%B1%28embedded-linux-system%29
相關連結:
http://hi.baidu.com/serial_story/blog/item/4bc67da7ed44f49fd0435807.html
http://www.ibm.com/developerworks/cn/linux/l-btloader/
http://gaznjang.blogspot.com/2008/04/u-boot-u-boot-bootloader-bootloader.html
一般來說,非必要的功能且可以編譯成為模組的核心功能,目前的 Linux distributions 都會將他編譯成為模組。 因此 USB, SATA, SCSI... 等磁碟裝置的驅動程式通常都是以模組的方式來存在的。 現在來思考一種情況,假設你的 linux 是安裝在 SATA 磁碟上面的,你可以透過 BIOS 的 INT 13 取得 boot loader 與 kernel 檔案來開機,然後 kernel 會開始接管系統並且偵測硬體及嘗試掛載根目錄來取得額外的驅動程式。
問題是,核心根本不認識 SATA 磁碟,所以需要載入 SATA 磁碟的驅動程式, 否則根本就無法掛載根目錄。但是 SATA 的驅動程式在 /lib/modules 內,你根本無法掛載根目錄又怎麼讀取到 /lib/modules/ 內的驅動程式?是吧!非常的兩難吧!在這個情況之下,你的 Linux 是無法順利開機的! 那怎辦?沒關係,我們可以透過虛擬檔案系統來處理這個問題。
虛擬檔案系統 (Initial RAM Disk) 一般使用的檔名為 /boot/initrd ,這個檔案的特色是,他也能夠透過 boot loader 來載入到記憶體中, 然後這個檔案會被解壓縮並且在記憶體當中模擬成一個根目錄, 且此模擬在記憶體當中的檔案系統能夠提供一支可執行的程式,透過該程式來載入開機過程中所最需要的核心模組, 通常這些模組就是 USB, RAID, LVM, SCSI 等檔案系統與磁碟介面的驅動程式啦!等載入完成後, 會幫助核心重新呼叫 /sbin/init 來開始後續的正常開機流程。
如上圖所示,boot loader 可以載入 kernel 與 initrd ,然後在記憶體中讓 initrd 解壓縮成為根目錄, kernel 就能夠藉此載入適當的驅動程式,最終釋放虛擬檔案系統,並掛載實際的根目錄檔案系統, 就能夠開始後續的正常開機流程。更詳細的 initrd 說明,你可以自行使用 man initrd 去查閱看看。 底下讓我們來瞭解一下 CentOS 5.x 的 initrd 檔案內容有什麼吧! ^_^
=======
因為一般PC上的linux實在太大了,所以有很多精簡化的東西,也有許多因為某些目的發展出來的專案,嵌入式系統比較少從無到有的,所以大多講究porting
boot loader : u boot
kernel : uClinux
root file system : uClinux、busybox
最後是兩篇人家網路上對新手的建議
http://phorum.study-area.org/index.php?topic=58481.0
http://jslinux.pixnet.net/blog/post/12065743-%E5%A6%82%E4%BD%95%E9%80%B2%E5%85%A5linux%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%B3%BB%E7%B5%B1%28embedded-linux-system%29
相關連結:
http://hi.baidu.com/serial_story/blog/item/4bc67da7ed44f49fd0435807.html
http://www.ibm.com/developerworks/cn/linux/l-btloader/
http://gaznjang.blogspot.com/2008/04/u-boot-u-boot-bootloader-bootloader.html
缺一不可教育訓練
因為嵌入式系統,讓我從新審視Computer Science的教育訓練,或許很多課程讓許多學生覺得可有可無,但是實際上都非常實用,我認為大學的教育是最為基礎與重要,相對的研究所應該是深入
- Programming Languages : 介紹語言特性以及Parser,這部分最貼近高階語言
- System Software : 重要的linker、loader、machine language...等等議題
- Compiler : 結合PL與System Software
- Operating System : 介紹作業系統的重要概念,input/output/cpu/memory/disk...,舉凡process、schedule...等等都在這裡
- Assembly : 機器相關的組合語言
- Data Structure : 幾乎大多C like的程式都用的到的,因為作業系統大多也是用C開發的
其他當然還有資訊概論、電腦程式語言(不管是basic/C#/C/C++/java...都是)等等都是很重要的
如果今天再看嵌入式系統,覺得有疑問,大概就是上面會有些東西遺漏掉了,一來畢竟是這東西很龐雜,再者就是老師們礙於時間有時候無法通盤解釋,最後就是學生們在累積相當知識之前,並無法串連這些東西
最上層可以看成一個作業系統,製作作業系統,必須要有程式語言來撰寫,當程式語言要轉換成machine language的時候,必須要有compiler,compiler先經過parser檢查code有沒有問題,在牽涉到linker以及loader轉換成對應的instruction codes/machine codes。這些codes處理作業系統中種種問題,為了讓程式有效率的執行必須要資料結構的輔助。
想不到一個嵌入式系統,又讓我重溫了過去大學教育訓練的東西。
2012年2月7日 星期二
linux上x86的process management(1)
主要來自兩本書
- "Understanding the Linux Kernel, Third Edition"
- "Professional Linux Kernel Architecture"
- 還有我的誤解XD,而且我只是做個讀書心得的摘要
以一般作業系統的角度,process是最基本的工作(task)單位,thread是後來更新的想法,所以基本上一般的OS還是對以process為基本出發點,再進一步以thread做管理(印象中M$號稱他們全部都是thread)
現在流行的CPU大多是以multi-tasking為出發點,也就是在概念上必須能夠同時執行多個task,可是以前one-core也好,到現在的multi-core也罷,工作項目大多遠遠超過core的個數,所以都是以快速工作切換的方式達到同時處理多個task,快到讓使用者沒有感覺
在切換task的時候,其實大多可以看成切換process,至於要挑哪個process,就交給重要的元件名為排程(scheduler)來完成,功能是挑選process放到CPU上執行,scheduler會對process的處理,會導致process處於不同的狀態,這些狀態也會反過來影響scheduler挑選的一些依據。
- create : 嚴格來說跟scheduler無關,是指process剛剛被建立,建立完成後就會通知scheduler,加入排程的隊伍(queue)
- wait : process可能還輪不到他執行,他在queue裡面等待
- suspend : process在等待某些資源,例如網路的封包或者硬碟檔案的傳輸
- running : process正在CPU上面執行
- stop : process已經完成工作,可能在等待資源回收
所以process的一生從建立後,scheduler讓process在queue內等待,到可能執行scheduler配給的cpu時間用完,又回到queue內,或者因為在等待某些資源等等的狀態轉換,到最後執行完畢。上面是一般高階的概念思考,Linux則有些更細緻的分辨以及實作上的考量。
Linux並沒有create的狀態,Linux透過fork()以及exec()兩個system call完成建立的工作,接下來task/process的狀態會被記錄在task_struct這個資料結構內有
- TASK_RUNNING : 正在執行
- TASK_INTERRUPTIBLE : 可能正在等待某個event/signal發生
- TASK_UNINTERRUPTIBLE : 在正等待kernel的某個event/signal
- TASK_STOPPED : 程式停止中,或許是時間用完,或許是被debugger中斷
- TASK_TRACED : 程式被trace當中
- EXIT_ZOMBIE : zombie死亡狀態(這是unix/linux的一種特別process結束的狀態)
- EXIT_DEAD : 正常結束
task_struct這個資料結構內容遠遠超出只有上面那些,大致上有
- 執行狀態(就上面提到的那些)
- memory資訊
- 一些process的必須資料,如process id (PID), group ID ...等等
- 使用中的檔案描述(File descriptor)
- thread的information(一些CPU相關的資訊)
- signal的handler
當程式(program)變成process(比方說大家同時在使用ls指令/program,你跟我在執行相同的程式碼,但是是不同的processes)的時候,一個process大致上可以被分成兩種模式user mode(ring 3)跟kernel mode(ring 0),雖然intel提供四種權限分類,但是linux只有使用兩種。兩種模式的差別在user mode程式只能存取自己user space內的東西,但是無法取得一些類似硬體的資源(比方寫入硬碟),如果要使用硬體資源,則必須透過system call或者interrupt(中斷)的方式,這時候kernel就會介入,在kernel監控下,讓使用者程式合理的權限允許下寫入,不合理行為是不被允許(例如把你的數值寫到他人的檔案內或者記憶體中)。中斷則由硬體產生或者程式自己安排的,例如印表機可用的時候告訴process,他想印東西,或者系統通知該process可能某些硬體出了一些狀況。
因為system call或者interrupt的關係,可能導致程式執行到一半被通知要處理其他事情,這時候大致上有三種處理方式
- 必須馬上處理的interrupt,要火速處理,該process被block,不管他是在user mode或者kernel mode
- 該process也在kernel處理要緊事,先把這件事情擱著,待會處理
- 雖然process在kernel的事情很要緊,但是process的事情的優先權低於該interrupt,先處理該interrupt
- 最後其實還有一個稱為kernel preemption的內容
作業系統在管理process的時候,以傳統的方式是記錄在一張表上,linux上面有一堆process,每個process都有自己一個獨一無二的ID編號(一個整數值),用以當作process的辨識號碼,但是隨著virtualization的盛行,已經不再只有pid,還有更複雜的namespace機制,以後再談。
當成是被建立之後,他必須配置一堆相關資源(包含PID),跟著在被sheduler藉由PID以及其他資訊加以排程進入CPU處理。
中間有個重要的過程就是建立的部分,包含了記憶體的配置...等等,主要牽涉到fork()、clone()、exec()等三個重要的system call
cmake入門(2)
主要參考wiki教科書上面,wiki教科書上的cmake入門是一個非常好的參考
首先先看架構
先開一個專案目錄,假設叫做hello好了,底下目錄架構如下
hello.c跟display.c就如我之前寫的,只是個示範用的,這裡重點是三個CMakeLists.txt檔案內容
src/app/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(hello)
include_directories(${CMAKE_SOURCE_DIR})
add_executable(app hello.c)
target_link_libraries(app display)
首先先看架構
先開一個專案目錄,假設叫做hello好了,底下目錄架構如下
- src
- app
- hello.c
- CMakeLists.txt
- display
- display.c
- CMakeLists.txt
- CMakeLists.txt
- build
hello.c跟display.c就如我之前寫的,只是個示範用的,這裡重點是三個CMakeLists.txt檔案內容
src/app/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(hello)
include_directories(${CMAKE_SOURCE_DIR})
add_executable(app hello.c)
target_link_libraries(app display)
src/display/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(display)
add_library(display display.c)
src/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
add_subdirectory(display)
add_subdirectory(app)
app目錄底下的CMakeLists.txt可以看到add_executable表示我們主要建立執行檔案所必須編譯的檔案,後面看到target_link_libraries表示需要連結的library
display目錄CMakeLists.txt的add_library表示要建立library,望文生義,至於要建立是static lib, dynamic lib or module請參考wiki文件,以後有機會再談,這裡建立的是static lib
src目錄下CMakeLists.txt很簡單的表示有兩個必須建立的項目
跟著回到build目錄底下,執行cmake ../src,cmake會讀取src目錄下的CMakeLists.txt去執行子項目,要在src底下執行cmake .也是可以,但是缺點是,所有編譯過程的檔案跟cmake產生的檔案會跑到src目錄底下,這樣某種程度"汙染"了src目錄的整潔度,應該讓src儘量保持乾淨,就放source code就好
2012年2月6日 星期一
ioctl的用法
一堆system call本身看似簡單,其實重要的應該是他的語意跟場合,知道如何使用
http://www.linuxidc.com/Linux/2007-12/9623.htm
http://tw.knowledge.yahoo.com/question/question?qid=1405112107181
我網誌上面有個有關lcd的使用方式,可以參考看看
http://linux2fork.blogspot.com/2012/02/lcdframebuffer2.html
http://www.linuxidc.com/Linux/2007-12/9623.htm
http://tw.knowledge.yahoo.com/question/question?qid=1405112107181
我網誌上面有個有關lcd的使用方式,可以參考看看
http://linux2fork.blogspot.com/2012/02/lcdframebuffer2.html
嵌入系統教學課程
想不到網路有這樣的一個東西,真是好物阿
http://opencsl.openfoundry.org/
另外再補一個網友使用其他平台的經驗談
http://blog.chinaunix.net/space.php?uid=23225855&do=blog&id=2353627
http://opencsl.openfoundry.org/
另外再補一個網友使用其他平台的經驗談
http://blog.chinaunix.net/space.php?uid=23225855&do=blog&id=2353627
[轉載]如何在ARM Embedded Linux實作automount功能
來自http://clc168.blogspot.com/2008/04/arm-embedded-linuxautomount.html
如何在ARM Embedded Linux實作automount功能
一般來說,目前我們桌上電腦當插入USB DISK,系統是使用udev來達成automount
在Embedded Linux中,我們並沒有跑udev這麼大的service,而是使用busybox的mdev
原理大概如下
Linux Kernel的hotplug -> 收到USB DISK insert/delete event -> 根據/proc/sys/kernel/hotplug指名的程式
-> 呼叫mdev -> mdev在根據/etc/mdev.conf設定檔指定batch file -> 執行相對應的/sbin/automount.sh
實作部份
Linux Kernel部份
================
在.config
將hotplug function打開
如果proc file system和sysfs file system沒打開的話也要記得打開(應該很少人會這個不開吧)
重新編譯kernel
Busybox部份
===========
很遺憾的Busybox 1.2版的mdev有bug,我是在Busybox 1.9版mdev source code發現已經fix這個bug了
Busybox的make menuconfig
請把mdev打開
P.S.
因為等下還有batch 需要grep 如果busybox沒開的話也要打開
target board file system部份
============================
vi /etc/inittab
掛載sysfs, 執行mdev, 並將mdev設為當系統收到hotplug event會去呼叫的程式
當然file system裡面要有/sys
vi /etc/mdev.conf
vi /sbin/automount.sh
在Embedded Linux中,我們並沒有跑udev這麼大的service,而是使用busybox的mdev
原理大概如下
Linux Kernel的hotplug -> 收到USB DISK insert/delete event -> 根據/proc/sys/kernel/hotplug指名的程式
-> 呼叫mdev -> mdev在根據/etc/mdev.conf設定檔指定batch file -> 執行相對應的/sbin/automount.sh
實作部份
Linux Kernel部份
================
在.config
將hotplug function打開
代碼: |
CONFIG_HOTPLUG=y |
如果proc file system和sysfs file system沒打開的話也要記得打開(應該很少人會這個不開吧)
代碼: |
CONFIG_PROC_FS=y CONFIG_SYSFS=y |
重新編譯kernel
Busybox部份
===========
很遺憾的Busybox 1.2版的mdev有bug,我是在Busybox 1.9版mdev source code發現已經fix這個bug了
Busybox的make menuconfig
請把mdev打開
P.S.
因為等下還有batch 需要grep 如果busybox沒開的話也要打開
target board file system部份
============================
vi /etc/inittab
代碼: |
ttyRI0::sysinit:/bin/mount -t sysfs sysfs /sys ttyRI0::sysinit:/sbin/mdev -s ttyRI0::sysinit:/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug |
掛載sysfs, 執行mdev, 並將mdev設為當系統收到hotplug event會去呼叫的程式
當然file system裡面要有/sys
代碼: |
mkdir /sys |
vi /etc/mdev.conf
代碼: |
sda1 0:0 0660 @mount /dev/sda1 /mnt sd.* 0:0 0660 @/sbin/automount.sh $MDEV |
vi /sbin/automount.sh
代碼: |
#! /bin/sh if [ "$1" == "" ]; then echo "parameter is none" > /tmp/error.txt exit 1 fi mounted=`mount | grep $1 | wc -l` # mounted, assume we umount if [ $mounted -ge 1 ]; then echo "R/media/$1" >> /tmp/usbmnt.log echo "R/media/$1" > /tmp/fifo.1 if ! umount "/media/$1"; then exit 1 fi if ! rmdir "/media/$1"; then exit 1 fi # not mounted, lets mount under /media else if ! mkdir -p "/media/$1"; then exit 1 fi if ! mount "/dev/$1" "/media/$1"; then # failed to mount, clean up mountpoint if ! rmdir "/media/$1"; then exit 1 fi exit 1 fi echo "A/media/$1" >> /tmp/usbmnt.log echo "A/media/$1" > /tmp/fifo.1 fi exit 0 |
linux底下用C語言建立dynamic library
dynamic library,顧名思義就是動態的,library並不如之前靜態的,在編譯時期就進入code,而是在執行的時候,再由ld.so去連結該library。為了管理與節省時間系統通常會對library做cache,由ldconfig指令來做一些操作,預設是讀取/etc/ld.so.conf裡的路徑做資料cache,可以透過ldconfig -v更新cache
檔案名稱規則是用lib開頭,附加.so,最後放上版本編號
gcc -fPIC -c -Wall display.c
gcc -shared -Wl,-soname,libdisplay.so.1 -o libdisplay.so.1.0.1 display.o
fPIC表示Position Independent Code
W1是W(one數字1)
這裡先釐清幾個事情
libdisplay.so (一般是一個 symbolic link,連到 libdisplay.so.1.0.1)
libdisplay.so.1 ( 一般是一個 symbolic link,連到 libdisplay.so.1.0.1 ) (有人稱它為soname)
libdisplay.so.1.0.1 (有人稱它為real name)
檔案名稱規則是用lib開頭,附加.so,最後放上版本編號
gcc -fPIC -c -Wall display.c
gcc -shared -Wl,-soname,libdisplay.so.1 -o libdisplay.so.1.0.1 display.o
fPIC表示Position Independent Code
W1是W(one數字1)
這裡先釐清幾個事情
libdisplay.so (一般是一個 symbolic link,連到 libdisplay.so.1.0.1)
libdisplay.so.1 ( 一般是一個 symbolic link,連到 libdisplay.so.1.0.1 ) (有人稱它為soname)
libdisplay.so.1.0.1 (有人稱它為real name)
一般來說,soname後面有個版本,相同的版本可以提供相同的介面才是,所以後面真實link到的是1.0.3或者1.0.1就可以忽略
libdisplay.so供連接器ld使用(當你用-l參數時),soname個供dynamic loader使用,最後一個是實際檔案。
一般我們都會將程式庫放在/usr/lib中,但通常我會都會依照GNU Directory VariablesStandard及FHS(Filesystem Hierarchy Standard)來決定程式庫的擺放位置,若根據GNU的標準所說,所有發佈式(非原本系統及系統服務額外所需的)的程式庫都應放在/usr/local/lib,系統管理員亦需加入/usr/local/lib為程式庫連結目錄,只有與系統有關的程式庫才會被放進/usr/lib;而根據FHS的標準而言,非系統服務用的程式庫也可放在/usr/lib之下的目錄裡。請留意GNU的標準與FHS並沒有衝突,因為GNU的標準是以程式庫開發者的角度來想,即是你需要一個外來的程式庫來發展你的程式時,你會把它放在/usr/local/lib;而FHS則是以使用者的角度來想,當你安裝一軟件時所需要的額外程式庫,則你多數會把它放在/usr/lib。在安裝程式庫時,你需把以real name命名的程式庫放在適當的目錄(/usr/lib或/usr/local/lib或其他,以後例子均使用/usr/lib),然後建立兩條分別以soname及linker name命名的symbolic link指向real name程式庫檔案,當然你也可以用ldconfig來協助你建立symbolic及cache,但ldconfig並不會為你建立以linker name命名的symbolic link, 而且自行建立看起來更快捷及可靠。
一般來說是這樣,因為dynamic library的概念就是把大家都用的到的code分享出來,所以必須放到大家都可以取得的地方,以linux來說,他會去搜尋/usr/lib跟/lib,跟/usr/local/lib,所以在編譯的時候連結跟靜態的一致,只要加上-L跟-l參數就可以
ln -sf libdisplay.so.1.0.1 libdisplay.so
gcc test.c -L./ -ldisplay -o test
第一道指令是給linker在編譯程式的時候用的,因為linker會去尋找libdisplay.so或者libdisplay.a,並不管版本號碼。
雖然編譯會過關,但是他並不能執行,主要是因為libdisplay.so.1.0.1並沒有放在預設的目錄下。執行時候ld.so會動態嘗試到預設的目錄下去找library,這時候就會找不到,發生執行的錯誤。一開始大家都會有個環境變數LD_LIBRARY_PATH,藉由更新他可以使用一個非正規的方式達到效果
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
這樣再去執行./test就可以了
參考資料:
http://blog.mchz.com.cn/?p=4590
http://www.yunsec.net/a/special/linux/application/2010/0606/4322_2.html
http://www.vr.ncue.edu.tw/esa/EmbeddedSystemProgramming2010/ch09.htm
http://blog.ykyi.net/2011/12/linux%E7%9A%84soname%EF%BC%8C%E9%93%BE%E6%8E%A5%E5%BA%93%E7%9A%84%E9%97%AE%E9%A2%98/
http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html
一般我們都會將程式庫放在/usr/lib中,但通常我會都會依照GNU Directory VariablesStandard及FHS(Filesystem Hierarchy Standard)來決定程式庫的擺放位置,若根據GNU的標準所說,所有發佈式(非原本系統及系統服務額外所需的)的程式庫都應放在/usr/local/lib,系統管理員亦需加入/usr/local/lib為程式庫連結目錄,只有與系統有關的程式庫才會被放進/usr/lib;而根據FHS的標準而言,非系統服務用的程式庫也可放在/usr/lib之下的目錄裡。請留意GNU的標準與FHS並沒有衝突,因為GNU的標準是以程式庫開發者的角度來想,即是你需要一個外來的程式庫來發展你的程式時,你會把它放在/usr/local/lib;而FHS則是以使用者的角度來想,當你安裝一軟件時所需要的額外程式庫,則你多數會把它放在/usr/lib。在安裝程式庫時,你需把以real name命名的程式庫放在適當的目錄(/usr/lib或/usr/local/lib或其他,以後例子均使用/usr/lib),然後建立兩條分別以soname及linker name命名的symbolic link指向real name程式庫檔案,當然你也可以用ldconfig來協助你建立symbolic及cache,但ldconfig並不會為你建立以linker name命名的symbolic link, 而且自行建立看起來更快捷及可靠。
一般來說是這樣,因為dynamic library的概念就是把大家都用的到的code分享出來,所以必須放到大家都可以取得的地方,以linux來說,他會去搜尋/usr/lib跟/lib,跟/usr/local/lib,所以在編譯的時候連結跟靜態的一致,只要加上-L跟-l參數就可以
ln -sf libdisplay.so.1.0.1 libdisplay.so
gcc test.c -L./ -ldisplay -o test
第一道指令是給linker在編譯程式的時候用的,因為linker會去尋找libdisplay.so或者libdisplay.a,並不管版本號碼。
雖然編譯會過關,但是他並不能執行,主要是因為libdisplay.so.1.0.1並沒有放在預設的目錄下。執行時候ld.so會動態嘗試到預設的目錄下去找library,這時候就會找不到,發生執行的錯誤。一開始大家都會有個環境變數LD_LIBRARY_PATH,藉由更新他可以使用一個非正規的方式達到效果
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
這樣再去執行./test就可以了
參考資料:
http://blog.mchz.com.cn/?p=4590
http://www.yunsec.net/a/special/linux/application/2010/0606/4322_2.html
http://www.vr.ncue.edu.tw/esa/EmbeddedSystemProgramming2010/ch09.htm
http://blog.ykyi.net/2011/12/linux%E7%9A%84soname%EF%BC%8C%E9%93%BE%E6%8E%A5%E5%BA%93%E7%9A%84%E9%97%AE%E9%A2%98/
http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html
2012年2月5日 星期日
linux底下用C語言建立static library
使用ar工具打包
static lib靜態,檔名使用lib開頭且已.a結尾,當編譯使用-l旗標的時候反而把lib拿掉
比方說檔案名稱libpthread.a,編譯的時候gcc main.c -lpthread
display.c
#include <stdio.h>
void display(){
print("Hello World!\n");
}
編譯指令
gcc -c display.c
ar -cvq libdisplay.a display.o
其中c表示create,v表示verbose,q是append意思,display.o後面還可以加入其他object檔案,打包成library
test.c
#include <stdio.h>
void display();
int main(){
display();
}
編譯指令
gcc -o test test.c libdisplay.a
或者(假設libdisplay.a放在相同目錄)
gcc -o test test.c -L./ -ldisplay
可以使用nm指令來觀察library所提供的函數(他是列出symbol list)
nm libdisplay.a
static lib靜態,檔名使用lib開頭且已.a結尾,當編譯使用-l旗標的時候反而把lib拿掉
比方說檔案名稱libpthread.a,編譯的時候gcc main.c -lpthread
display.c
#include <stdio.h>
void display(){
print("Hello World!\n");
}
編譯指令
gcc -c display.c
ar -cvq libdisplay.a display.o
其中c表示create,v表示verbose,q是append意思,display.o後面還可以加入其他object檔案,打包成library
test.c
#include <stdio.h>
void display();
int main(){
display();
}
編譯指令
gcc -o test test.c libdisplay.a
或者(假設libdisplay.a放在相同目錄)
gcc -o test test.c -L./ -ldisplay
可以使用nm指令來觀察library所提供的函數(他是列出symbol list)
nm libdisplay.a
uClinux, skyeye模擬器 and Linux kernel的陷阱
根據網路上的說法uClinux通常用於缺乏MMU的arm cpu上面,一般開發embedded system通常利用一般電腦編譯成arm cpu所需的格式在上載或者包裝成rom後,再把程式放到機器上
機器開發版有時候也是不便宜,再者有時候有些硬體平台相關性,這方面也是挺花精神的,網路上有人開發出免費的模擬器skyeye
所以先到uClinux上下載uClinux-dist套件,跟著也要下載相對應的tool chain,這時候要注意一件事情,請選擇相對應的tool chain跟uClinux-dist版本跟skye也一樣(google到教學文章可以參考,版本相近的相容性應該還好)。比方說2005左右對應2003的tool chain,新的2011對應最近的tool chain,這樣編譯起來比較輕鬆。skyeye也是,舊的2005他們大概可以用1.2.x執行,可是最近的linux-3.x可能使用skyeye 1.3.x比較好。
不然就準備要面對一堆compile flag不支援的問題(gcc 2.9.x vs gcc 3.x/4.x),或者編譯出來的image放到skyeye上執行有問題。
老實說,花很多精力在這方面感覺有點不值得orz
機器開發版有時候也是不便宜,再者有時候有些硬體平台相關性,這方面也是挺花精神的,網路上有人開發出免費的模擬器skyeye
所以先到uClinux上下載uClinux-dist套件,跟著也要下載相對應的tool chain,這時候要注意一件事情,請選擇相對應的tool chain跟uClinux-dist版本跟skye也一樣(google到教學文章可以參考,版本相近的相容性應該還好)。比方說2005左右對應2003的tool chain,新的2011對應最近的tool chain,這樣編譯起來比較輕鬆。skyeye也是,舊的2005他們大概可以用1.2.x執行,可是最近的linux-3.x可能使用skyeye 1.3.x比較好。
不然就準備要面對一堆compile flag不支援的問題(gcc 2.9.x vs gcc 3.x/4.x),或者編譯出來的image放到skyeye上執行有問題。
老實說,花很多精力在這方面感覺有點不值得orz
2012年2月4日 星期六
cmake入門(1)
簡單的說,要先安裝cmake這工具才能開始,這東西主要幫主程式設計師產生Makefile,Makefile是一堆編譯步驟的集合,從source code到binary過程很簡單,但是程式愈寫愈多的時候,過程就會有點繁瑣,所以Makefile就衍生出來,不過Makefile其實也有點痛苦,另外一開始是有平台相關性的,有人就想把這工作在簡化,衍生出cmake這工具
介紹結束,首先準備一個目錄,來存放source code與cmake需要的檔案,分別就是helloworld.c跟CMakeLists.txt這兩個,所以這目錄底下就只有這兩個檔案
底下是CMakeLists.txt的內容,至於helloworld.c這程式碼自己寫吧,裡面其實只是印出hello world字串
接著如果說你一切都沒有寫錯,就是跟著兩個指令
cmake .(注意有個點)
make
結果類似這樣囉,跟著就可以執行./helloworld
可以參考舊有工具automake跟autoconf,http://os.51cto.com/art/201003/185711.htm,有點小複雜
如果專案不大,最佳的方式還是直接gcc helloworld.c -o helloworld
FILE(GLOB Mac_CPP “*_Mac.cpp”) FILE(GLOB Mac_H “*_Mac.h”) LIST(APPEND Mac_Sources ${Mac_CPP} ${Mac_H}) 上次討論到的. 怎麼把file list抓下來,然後不用一個file一個file加入的方式
參考:
http://andescore.blogspot.com/2009/07/cmake.html
http://zh.wikibooks.org/wiki/CMake_%E5%85%A5%E9%96%80
http://blog.csdn.net/dbzhang800/article/details/6314073
介紹結束,首先準備一個目錄,來存放source code與cmake需要的檔案,分別就是helloworld.c跟CMakeLists.txt這兩個,所以這目錄底下就只有這兩個檔案
底下是CMakeLists.txt的內容,至於helloworld.c這程式碼自己寫吧,裡面其實只是印出hello world字串
接著如果說你一切都沒有寫錯,就是跟著兩個指令
cmake .(注意有個點)
make
結果類似這樣囉,跟著就可以執行./helloworld
可以參考舊有工具automake跟autoconf,http://os.51cto.com/art/201003/185711.htm,有點小複雜
如果專案不大,最佳的方式還是直接gcc helloworld.c -o helloworld
FILE(GLOB Mac_CPP “*_Mac.cpp”) FILE(GLOB Mac_H “*_Mac.h”) LIST(APPEND Mac_Sources ${Mac_CPP} ${Mac_H}) 上次討論到的. 怎麼把file list抓下來,然後不用一個file一個file加入的方式
參考:
http://andescore.blogspot.com/2009/07/cmake.html
http://zh.wikibooks.org/wiki/CMake_%E5%85%A5%E9%96%80
http://blog.csdn.net/dbzhang800/article/details/6314073
開版^.^
很簡單的是因為對linux kernel的好奇,以及本身會寫點程式開始這個版,主要就是對自己的資料收集以及讀書資料作一個紀錄
話說萬事起頭難,小小的插曲是,本來要開始寫東西的時候,想不到遠端就在短短三伍分鐘內當機了,以至於連不上去,造成了一兩個小時的擱置,真的是有點好笑,人生路上總再不經意的地方給你來個插曲。
話說萬事起頭難,小小的插曲是,本來要開始寫東西的時候,想不到遠端就在短短三伍分鐘內當機了,以至於連不上去,造成了一兩個小時的擱置,真的是有點好笑,人生路上總再不經意的地方給你來個插曲。
訂閱:
文章 (Atom)