發表文章

目前顯示的是 2007的文章

Linux Kernel Patch: 使用 sysctl 控制 idle method

到了今天,SMP系統已經很常見,街上隨便抓一台 NB 都是雙核心的架構。但也因為是 NB,又常搭配 NVIDIA 的顯示晶片,我們必須對 idle method 做適當的調度,以保持其穩定性和續航力。在過去,我們在開機就必須指定 BootPrompt 以決定 idle method,這對 LiveCD 和各 Linux 套件實在不是很好,因為在 Boot Loader 時,我們並不曉得該台電腦的狀況,無法判斷是否該用 poll idle 的方式啟動 Kernel。

因此,我針對這問題對 Linux Kernel 做了一個 patch ,讓用戶可以在進入作業環境後,從 /proc/sys/kernel/ 中即時開關 poll_idle(),以便硬體偵測程式可以依系統需求,動態去啟動 poll_idle()。

這次的 patch 可由此下載:linux-kernel-2.6.23.9-idle_poll_sysctl.patch

老莊有理,惡搞無罪

哲學是非常奧妙的一門學問,它探討了這宇宙的構成與真理,卻又不涉及尖端科技的理論,完全以邏輯的方式證明這個世界。如果說現在最先進的科技,是尋找無限放大的世界,那麼,最高等的哲學就是尋找無限縮小的世界。這兩種世界從字面上分析是相反的,但我們卻不難體會出其中兩者一體的絕妙之處。中國道家學說就是建立在這巨觀的基點上,常聽到的莊周夢蝶,就是這麼一回事。

雖然在今日,眾多西洋哲學林立,其中也不泛有好的理論,但我個人總是偏好道家莊子一派。大家都曉得道家是以老、莊為代表人物,歷史上總是以『清淨無為』為定位,所衍生出來的『無為而治』解釋更鬧出不少笑話。我無法對後人的譯文做評論,但我相信如果老子對自己所創造的學說了然於胸,他口中的『無為而治』就並非後人所解釋的『無為而治』﹝因為後人對這句話的認知和道家學說本身矛盾﹞。先不論老子是否有自相矛盾之舉,莊子在這方面表現的較無爭議,因此個人較偏好莊子一派。

道家認為世界萬物,都是處於道德之中﹝並非仁義道德的道德,簡單來說道德就是包括時間、空間和所有東西的大宇宙﹞,我們活在這大宇宙的洪流之中,隨著時間、萬物一同漂蕩。也就是說我們是被這大宇宙所掌握,如同我們掌控手中的一杯拿鐵一般。別以為你擁有不被掌控的選擇,因為你的身體、心靈、智慧、想法都是在這宇宙的洪流之中產生,冥冥之中,你所有的一切都不是你的,我們的想法其實是道德要我們產生的想法,我們的所作所為也是道德要我們去做的,換言之,我們的一切,就是道德的一切,也是道德透過我們在做的一切。

曾經就有人斷章取義,以『既然我無法決定我自己,那大家都擺爛,看這世界會如何。』試圖證明道家學說的荒謬。看起來這個人說的話好像是正確的,但其實他弄錯一點,大家擺不擺爛,也是由道德所決定,因為除少數人外,你我都不會有意願擺爛,他的假設也就不會成立。

其實,用個簡單的邏輯就可以很容易明白這道理。

道德掌控人 → 人掌控想法 → 人想改變道德


結論是,除非道德本身要改變,否則人是無法躍過層級去改變道德的。

如果我們將道德用『神』一詞取代,道家的觀點有點神論的味道,但更激烈的是道家認為這世上只有神的存在,人根本不算個東西。其實,將道德換成『基因』就更明瞭了,但多數人只可以接受有東西在控制他的身體,卻不接受有東西在控制他的想法﹝因為人都覺得自己可以改變一切呀!﹞。

講了這麼多,道家學說到底要告訴我們什麼呢?其實就是純開槓、打屁。只有你去忘記道…

關於 Linux BootPrompt "idle=" Argument

關於 Linux 的核心開機參數 idle,在之前的文章『不必灰心!NVIDIA GeForce Go 7300 的擁有者!』已經使用過,不過當時只知道可以用來解決某些 NVIDIA 顯示晶片的問題,並不曉得其真正用途與目的。

經過查詢後,在 TLDP 找到了 BootPrompt HowTo,並在『 General Non-Device Specific Boot Args 』章節找到了一段 idle 參數的說明。

Setting this to 'poll' causes the idle loop in the kernel to poll on the need reschedule flag instead of waiting for an interrupt to happen. This can result in an improvement in performance on SMP systems (albeit at the cost of an increase in power consumption).

設 idle=poll 會使 kernel 使用 idle_poll() 取代原有 idle() 的 HLT instruction,提高 SMP 系統的運算效能。另外,因為 idle_poll() 的機制會使 CPU 使用率變高,所以相對的也會耗費更多的電力。如果是單核心 CPU 的環境,就不用考慮了這個機制了,基本上在單核心的環境之下使用 idle_poll() ,只會造 CPU 更大的負擔,效能可能會更差。

現在想想,之前 NVIDIA GeForce Go 7300 也是在雙核心的 CPU 上跑,或許是因為官方的 Driver 對 SMP 的支援還有問題。

不必灰心!NVIDIA GeForce Go 7300 的擁有者!

這兩天使用 ASUS A8T NB 做系統的支援度測試,如預期的一切都很正常,唯一有問題的是,系統死當!這死當不只是 Xorg 當掉,也不只是 OS Kernel Crash,是整個電腦忽然間 Freeze!﹝自從 Win95/98 後已經幾乎沒碰過這樣的狀況了﹞

經過 Debug 後,發現是 NVIDIA 官方驅動和 Compiz 所造成的。但無論我升級成什麼版本的 Driver﹝我已經衝到12/20才剛出來的 169.07 版﹞,一樣是死掉。上網尋找是否有人碰到跟我相同的狀況,結果只找到各式各樣使用 NV 6200/7200/7300 所碰到的問題,當然和碰到跟我一樣狀況的人也是有,但都是無解。

好吧,我該放棄了。正準備將 ASUS A8T NB 加入到不支援名單時,看到一篇 ASUS A6T 的 Linux 使用報告﹝http://gentoo-wiki.com/HARDWARE_Asus_A6T﹞,由於 A6T 所使用的顯示晶片和 A8T 是一樣的,所以我很認真的讀完了它。文件中所說的重點,我大致上都了解,但有一點比較特殊,是在 Boot Prompt Command 加上『idle=poll』,文件中說這可以增進使用 GPU 的系統穩定度。於是活馬當死馬醫的我就照做了,哈哈!Bingo!問題終於解決!

碰到跟我一樣問題的人可以試試這方法!NVIDIA GeForce Go 7300 的擁有者們!

後記

提醒我,該去查查 idle=poll 到底是做什麼用的。

lxpanel Patch: 修正與 compiz cube 的相容性問題

電腦的演進神速,轉眼就到了 3D Desktop 的時代。雖然許多人追求輕便型的桌面環境,但如果不嘗嘗 CompizFusion 所帶來的超酷視覺享受,似乎對不太起今日的電腦硬體 :P

這幾天發現 lxpanel 和 compiz cube 有相容性的問題,也就是 viewport 不會跟著 active 的視窗轉動。這問題牽涉到 EWMH 規範的操作,簡單修正後 lxpanel 的 taskbar 已經能正常切換工作並更改 viewport 狀態。

想修正的人可以使用這 patch:lxpanel-0.2.4-viewport.patch

oxim Patch: 更進一步改善 X event 處理機制

關於 oxim 的 xevent 處理機制所造成的 bug 在前一次 patch 時已經提過﹝請參閱『oxim Patch: Event BUG』﹞,由於網友 swyear 的告知,也意外的發現在 openSUSE 10.3 上有相同原因所造成的困擾。

經過這兩天測試後,發現前一次的 patch 雖然解決了無法正常啟動 oxim 的問題,但是其運作的並不穩定,有死掉、呆掉的情況發生,而且造成的 CPU 負擔甚大。所以,這次用 select 更進一步改善了 oxim 對 X event 的處理機制。

關於這次的 patch:oxim-1.1.3-xevent-20071223.patch

oxim Patch: Event BUG

最近移植 oxim 時碰到一個問題,就是當我使用 xinit 啟動 X Server 和 oxim 時,oxim 會 wait 在那動都不動,使用 strace 也無法 debug,因為一旦以 strace oxim 這樣的方式啟動 oxim,它就沒這個問題。﹝什麼!?這 Bug 還會自動躲 debug 程式!﹞

一邊努力 debug 一邊在 IRC 上抱怨,最後終於靠 jserv 的建議與指導之下,用了 gdb + XSynchronize() 找出了出問題的地方,果然皇天不負苦心人啊!

問題出在 oxim 對 gui event 的處理,大概是使用 xinit 直接啟動的環境太簡單乾淨,oxim 的 Trayicon 等需要 GUI 的部份完全收不到 X 的 event,所以就停在 XNextEvent 苦命等待。

不過就在解決掉 event 等待的問題後,我將 debug 時用的 XSynchronize() 移除,隨即碰到的就是 GUI 重繪問題。雖然 oxim 順利的啟動了,但選字區、選單都一片空白,將 XSynchronize 開啟就正常,這點非常怪異令我不得其解,不過想到 oxim 只是處理使用者輸入中文,對 graphic 的處理速度並不非常要求,所以我將 XSynchronize 保留。

關於這次的小修正,我已經做成 patch:oxim-1.1.3-xevent.patch

OrzMicrokernel main.asm 核心啟動主結構

作業系統核心的運作分兩個階段,第一個階段是初始化,第二個階段是規律的活下去。從第一階段到第二階段的流程和主結構都清楚地寫在 main.asm,而工作排程、記憶體規劃等設計都是由此再延伸出去。

main.asm 原始碼:
%include "defines.asm"
%include "orzmicro.inc"
%include "16bit.asm"
%include "32bit.asm"

;-----------------------------------------------------------------------------------
; Now, we are under 32 bit protected mode. Starting...

mov al,1 ; 設定為 320x200 的圖形模式
call SetVideoMode ; 呼叫 video.asm 的子程式設定顯示模式

call InitTasks ; 呼叫 tasks.asm 的子程式初始化工作狀態記錄表

mov eax, Ts1 + 0x10 ; 設 EAX 為工作記憶體開始位址
mov ebx, Ts2 - Ts1 - 0x10 ; 設 EBX 為工作所佔記憶大小
mov ecx, [Ts1 + 8] ; 設 ECX 為工作二進位檔的第 8 個 bytes 所設定的堆疊大小
mov edx, [Ts1 + 12] ; 設 ECX 為工作二進位檔的第 12 個 bytes 所設定的資料大小
mov esi, ts1Name ; 設 ESI 為工作名稱
call CreateTask ; 呼叫 tasks.asm 的 CreateTask 子程式建立工作

mov eax, Ts2 + 0x10 ; 設 EAX 為工作記憶體開始位址
mov ebx, Ts3 - Ts2 - 0x10 ; 設 EBX 為工作所佔記憶大小
mov ecx,…

Aufs Overlap Check Bug

Aufs 是一個能結合各種 FileSystem 到同一個分區的聯合檔案系統機制,它在這兩年內快速竄起,取代了其 Unionfs 的地位。而關於其歷史背景和較詳細的介紹,有興趣的人可以參考我過去的文章『取代Unionfs的好東西 - Aufs』。

還記得我最後一次 Compile Aufs 是在去年底今年初的時候﹝抱歉。詳細日期記不太清楚﹞,使用 CVS 直接從 SourceForge 取得的最後版本﹝Aufs 專案本身沒有提供打包好的 Source Package 只能用 CVS 取得﹞。Aufs 被我使用在當時正在研發的一些 Embedded 系統內,將原本的 rootfs 放在 Aufs 之上以得到更多的擴充性。我將所有的 FS-Modules 藉由 loopback 而 mount 到 /dev/unionfs/[0-X] 之中,最後再用 Aufs 將這些目錄與 rootfs(/) 做結合。那時候一切都很正常,我也很滿意。

這幾天,因為舊 Linux Kernel 許多的 Bug 和缺少某些 Device Drivers 的情況下,我只好將 Kernel 更新並重新 Compile 來一勞永逸,順帶了也更新了一些 patch 和功能的版本,而 Aufs 就是在這幾天被我更新的。可是當一切準備就續後,卻發現 Aufs 在我的系統上總是出現 Overlapped 的訊息,它不讓我將 /dev/unionfs/[0-X] 加入到根目錄的 Aufs 上。之後搞了半天,才發現 Aufs 這幾個月的更新,使檢查 Overlap 的部份有錯誤。

於是,我直接將檢查 Overlap 的 function 的 return,改成 Always 回傳沒問題,這問題就解決了。當然,這是『不正確』的方法,我只是讓 Aufs 跳過不檢查是否有 Overlap 的情形發生。要是碰到出狀況,無限循環會讓系統整個大亂,到時候就欲哭無淚。

我有羽毛‧我會展翅

剛跟父親吵過一架,我們吵架不外乎就是對某些事的見解不同。近年來除非是突如其來的大吵,多半時候只是耳聞不回應,就這樣避開了取多爭執的機會。

老實說,我老是被父親抓得緊緊的,保護的異常的好,各方面的起步與發展,也都是因為他而啟蒙。從他身上,我的確學會了很多平常人沒機會接觸的東西與經驗,也看到了許多未來的目標和前景。就因為這些不同的經驗和想法,無形之中自我限制了不少事情,因此有很多時候不像同年紀的人一般自在,但也讓我感受到這世界的多采多姿。

我慢慢長大,雖然就全方位來說,我仍是生澀的小毛頭,但就部份成果來說,我卻也有一定的功力和能力,尤其某些特定領域,我更是投注完全精力去一窺天地。不知不覺,在這些部份,已經漸漸超出父親所能掌控了解的範疇。一切來的這麼不著痕跡,當然在這兩方面見解所產生的爭議,也越來越頻繁。

就某些領域,父親雖然已經不能再提供我更多知識,但是他嘗試告訴我更多的心,我很感激。可是父親的不放手,卻也讓我步步艱辛,彷彿我了解更多,我就有更大的問題一樣。如此而爭吵,一點都不必要。羽毛長出來了,卻要我一根根拔掉,更是不必要。怕我著涼,又為我植下更多的羽毛,這又是何苦?

我曾經嚮往奔放體育,嚮往揮灑藝術,嚮往打滾人際,但是,如今卻一一捨去。因為你讓我瞭解,如此盲目嚮往是沒有未來的。今天,我只是個在大型溫室中的花朵,巢中的殘雉,已經沒有了過去的一切。如此的我,能不堅持僅存的一些理想和想法嗎?

實作 Linux 下的硬體自動偵測:X.Org 驅動程式

關於自動偵測 PCI 裝置實作的部份,在『實作 Linux 下的硬體自動偵測:PCI 裝置』已經提過,應該有九成以上的硬體,都可以如預期的被辨識且載入適當的驅動模組。但是,那只限於 Linux Kernel 的驅動模組被正確配置而已,對於常用到的顯示卡而言,能正確設定 X.Org 的設定檔 xorg.conf 才算真正達成自動偵測的目的。﹝傳言新版的 xorg.conf 已不需要再特別設定,但我沒試過,還請用過的人能告知小弟。﹞

相對 Linux Kernel 有 modules.pcimap , X.Org 有 *.xinf 可以讓我們比對顯示卡 Vendor:Device 和 xorg driver 的關係,可以在 /usr/share/hwdata/videoaliases/*.xinf 找到一系列 xorg 顯示卡驅動程式的對應資料檔。

舉例來說 opensource nvidia driver 對應到的 nv.xinf,檔案資料的格式大致如下:

alias pcivideo:v000010DEd00000427sv*sd*bc*sc*i* nv # g80

其中 v000010DE 和 d00000427,就是硬體的 Vendor:Device。由此,我們就可以仿照之前偵測 PCI 裝置的方法,找到顯示卡對應的 xorg driver。

可以參考這段簡單的 Script Function:
# findxorgmodule [vendor] [device]
findxorgmodule() {
# Transforms lowercase characters into uppercase
VENDOR=`echo -n ${1} | tr [a-z] [A-Z]`
DEVICE=`echo -n ${1} | tr [a-z] [A-Z]`
for VIDEOINF in /usr/share/hwdata/videoaliases/*.xinf; do
while read HEADER DEVICEID MODULE COMMENT; do
if [ x"$HEADER" = x"alias" ]; then
echo $MODULE

實作 Linux 下的硬體自動偵測:PCI 裝置

x86 的硬體分為很多種類別,從早期的 ISA 一直到後來的 PCI、PCMCIA、USB,都有各自的硬體接角定義,要辨識這些裝置,得靠這些硬體通電與主機板連接後,所提供的自身資訊。

就最常用到的 PCI 裝置來說,目前有 99.999% 的硬體都是歸屬於 PCI 裝置﹝包括AGP、主機板上的各種控制晶片﹞。所以,只要能夠正確辨識 PCI 裝置並載入相對應的 Modules,就能夠對大部份硬體做自動識別。

識別 PCI 的方法,就是讀取 /proc/bus/pci/devices 或 /sys/bus/devices/* 取得裝置所提供的 vendor(製造商)、device(裝置或晶片型號)、subvendor(再製造商)、subdevice(再製造之裝置型號),然後將這些資訊與 Linux kernel 所提供的 modules.pcimap 做比對,以得到該裝置所對應的 module name。最後,只要使用 modprobe 去載入模組就大功告成了。

比較要注意的是 modules.pcimap,如果你詳細研究過這 map 檔,應該會發現同一個 vendor:device,卻偶爾有兩種可使用的 modules。例如使用 10ec:8139 去尋找,會同時找到 8139cp 和 8139too。諸如此類的裝置非常多,所以比對 modules.pcimap 時有其規則以免發生這樣的狀況:

比對的優先順序應該是:
尋找 vendor:device 和 subvendor:subdevice 相同的專用模組。尋找 device 和 subvendor:subdevice 相同的通用模組。尋找 device 建議模組。尋找 vendor:device 標準模組。

使用 bash 完成的程式碼:
#!/bin/bash
MODULEMAP=/usr/share/frexhwd/modules.fhm

# findmodule [vendor] [device] [subvendor] [subdevice]
findmodule() {
while read VENDEV SUBVENDEV MODULE; do
if [ x"${3}${4}" = x"$SUBVENDEV" ]; then
echo $MODULE

Frexhwd 嬌小的硬體偵側程式

今年初的時候,我在 SourceForge 申請了一個 Project 叫做『Frexhwd』,這是當時我寫給這專案的一段描述:

Frexhwd is a simple and small Hardware Detection for Linux. It is written by Unix
shell(Bash) and C Language. it easy to run on small, embedded or simple system.

由這專案的名稱就可以知道,這是一個硬體偵側程式(Hardware Detection),而當初建立這專案的目標就是希望以最簡單的方式,實作硬體偵測並自動載入相對應的 Drivers。其實說到類似的功能,Debian 的 discover 與 Redhat/Fedora 的 Kudzu 都已經做得相當完整,而且在實際運作上都已經沒有什麼問題了。但如果真要說令人不滿意的地方,就是太過 fat!

discover 除了在非 Debian 系的環境中 compile 會碰到不少問題外,他的妸娜的身材倒還可以接受;而 Kudzu 從一開始就選擇 Python 當撰寫語言,根本是個錯誤。正因為 Python 的相關 Librarys 和檔案,本身就已經是個肥大的腫瘤,令使用它的 Kudzu 無法輕巧的移植到其他簡單的系統中。

最近又再次接觸到許多小型系統的研發,發現許多程式應該回歸其應用本質和單純,包山包海的結果必然會和複雜度同時產生,除了維護、移植的問題外,更有許多環境限制和浪費資源在非必要的功能,當初會建立 Frexhwd Project 也是因為有這樣的想法。

我希望 Frexhwd 是以 Shell 和 C Language 撰寫,一方面使用 Shell 可移植度高,另一方面,可以使用 C 補強 Shell 在檔案資料處理上的效能不足。不過照目前的進度來看,還未使用到 C 來撰寫任何一部份的程式,僅用到 Busybox 或 GNU 的 grep 來達成硬體模組的資料比對。

雖然遲遲沒有將 Frexhwd 的 source code 放上去,但已經有一些成果,等最近忙一段落,會將相關檔案整理好放上 SourceForge。

使用 Bash Shell 撰寫 Web CGI 程式

使用系統的 Bash Shell 來寫 CGI,這是以前做嵌入式系統時的一個 Web 應用程式解決方案,構想很酷、很有趣。當時考慮到,如果用 C 語言寫 CGI,程式會既複雜又不易維護﹝研發嵌入式系統時 Cross Compile 和繁瑣的 Debug 可是很討厭的!!﹞;若是讓 Web Server 其支援 PHP、Perl,其程式架構會太過龐大。種種因素讓我決定用 Bash 完成需要的 CGI 網站應用程式,畢竟 Bash 從面市到現在,功能越來越齊全強大了,我想應該能勝任 Web Scripts 的工作!:P

當時我先上網搜尋是否有人試過使用 Bash Shell 開發過 CGI 程式,果然有個老兄寫了一些 Bash Functions 來支援些 Web 上的操作,讓我們可以輕易使用 Bash 來寫 CGI。有興趣的人可以到 bashlib(http://bashlib.sourceforge.net/) 抓回來玩玩看。

如果你看過了這個 Bashlib ,一定發現其實它功能很陽春,只有取得 GET 傳值、表單傳值、轉向、Cookies等幾種功能。嘿嘿,這時候當然要開始惡搞這個 bashlib,我改寫並加入了一些功能,像是 Session 的支援。

自己額外亂搞新增支援 Session 的 Functions:
function init_session {
if [ ! -d /tmp/.xcdmgrsess ]; then
mkdir /tmp/.xcdmgrsess &> /dev/null
fi

SESS_ID=`cookie SESS_ID`
if [ A$SESS_ID = A"" ]; then
SESS_ID=`mkcode 30`
set_cookie SESS_ID $SESS_ID
touch /tmp/.xcdmgrsess/$SESS_ID
else
if [ -f /tmp/.xcdmgrsess/$SESS_ID ]; then
while read SESSLINE; do
eval "export 'SESSION_${SESSLINE}'"
done < /tmp/.xcdmgrsess/$SESS_ID
else
touch…

有趣的邏輯問題:是誰在說謊

開心的是期中考接近尾聲,而且今天也是我的生日,更開心的是老天爺待我很好,放我一天假沒給我排考。今天,在網路上逛啊逛,看到了一些判斷說謊的邏輯問題,雖然之前就曾看過,但不管再看多少遍都還是覺得很有趣。也想到好幾天沒寫東西了,就貼上來讓大伙一同分享吧!準備好接受考驗了嗎? Ready? Go!

第一題:

A 說B 在說謊,B 說 C 在說謊,C 說 A、B 都在說謊。請問到底誰在說謊?



第二題:

現在審問四名竊賊嫌疑犯。已知當中有一名是竊賊,還知道這四個人不是誠實就是說謊,請根據他們回答問題的結果中,判斷誰是竊賊。

甲說:『乙沒有偷,是丁偷的。』
乙說:『我沒有偷,是丙偷的。』
丙說:『甲沒有偷,是乙偷的。』
丁說:『我沒有偷。』



解答:一:只有 B 說真話。二:乙是竊賊。

OrzMicrokernel tasks.asm 尋找工作函式註解

到目前為止,從 Orz Microkernel 運作流程來看,我們已經看過主要的程式碼了,剩下的部份就只有些零零星星的函式和各種服務程式的實作。其中最重要的就是 Orz Microkernel 自訂的 IPC 訊息中斷服務(Message Service),該服務會運用到工作處理方面的函式,所以現在先將工作相關的函式註解補齊。

程式碼和詳細註解:
;------------------------------------------------------------------------------
; 尋找工作 FindTask
;;;; 輸入值: EAX = 工作編號 ID
;;;; 返回值:若進位旗標 CF=1 則表示找不到工作,否則 EAX = 工作狀態編號,EBP = 工作狀態指標
;;;; 會影響到的暫存器:EAX, ECX, ESI, EBP

FindTask:
mov ecx, [tasksCount] ; 設 ECX 為工作總數量
jecxz FindTask1 ; 若 ECX 為零,則跳到 FindTask1

mov esi, tasksList ; 設 ESI 為工作清單的記憶體位址

FindTask0:
movzx ebp, BYTE [esi] ; 從 ESI 取 2 Bytes(工作編號)到 EBP
imul ebp, s_task.msize ; EBP = EBP x s_task.msize
add ebp, tasks ; EBP = EBP + 工作狀態記錄所在記憶體位址

cmp eax, [ebp + s_task.taskID] ; 若 EAX = s_task.taskID 則零值旗標 ZF = 0
jz FindTask2 ; 若零值旗標 ZF = 0 則跳到 FindTask2

inc esi ; ESI 加一
loop FindTask0 ; 迴圈 ECX 次,繼續尋找工作

FindTask1:
s…

Orz 天殺、惡搞的 Microkernel

昨天在 IRC 上與 Jserv 有談到一點 Orz Microkernel 的問題,因為在註解的過程中,我碰到不少挫折。內容談到 Orz Microkernel 本來就是一個玩具,當初被寫出來就只是 Just Fun!,所以裡面常會出現 0x12345678 這種奇怪沒道理的定義,更有許多 Magic Code 和惡搞的陷阱,像是對目前核心沒意義的偏移之類的,這部份讓我真的被玩弄了,有強烈 Orz 的感覺﹝Jserv 成功了!囧﹞。

雖然這些惡搞的 Code 令我傻眼,但一一去發現它也不失為一個好的休閒活動﹝笑﹞! Jserv這次有說到,因為聽說真的有大專院校的學生在研究 Orz Microkernel,所以打算在下一版的 Orz Microkernel ,做精簡化、優化等等的動作,並且藉此拿掉惡搞東西,可能還會加入 GUI、Function call 等東西進去,想必許多研究 Orz Microkernel的人應該也跟我一樣看到很多傻眼的 Code 吧!!

好像要遲到了,該準備去上課囉!我是個很混的大學生,好像讀書都沒讀 Orz Microkernel 的麼的認真說。:)

被誤解的可悲稱號:電腦高手

在你心中,所謂的『電腦高手』是怎麼樣的呢?會 DIY 組裝電腦?了解硬體行情?灌 Windows 作業系統?懂得如何處理各種商業軟體的問題?還是會寫程式?救中毒或壞掉硬碟內的資料?更或是會入侵、從網路偷別人隱私?我自認不是什麼電腦高手,充其量只是朋友抬舉,電腦方面的接觸只是兼職的工讀,但常聽到很多人的話令我不知如何是好。

『某某某電腦很強,要買什麼硬體比較划算,問他就好啦!』

就是這簡短的一句話,讓我有勤練十年功而手腳筋全斷的感覺,如果說知道硬體的價格是要當電腦高手的必要條件,那我想電腦賣場中的採購、會計、業務員是天底下最厲害的武林至尊了。

『怎麼會?你電腦不是很厲害,一定知道吧!?』

大家都知道,微軟的作業系統最令人無奈的問題,就是從早期的 Always 藍色死當,到後來的『程式關閉,請洽...』,再到現在的『程式無回應』。說無奈是因為我們根本無從得知,為什麼程式或系統會突然死掉,或是某些商業軟體在設計上有什麼暇疵;還有最重要的是,微軟系的病毒嚴重肆虐,已經讓人不想再去了解病毒的變化,能殺且殺,不能殺重灌便算。還有 Office 的操作,各種軟體的使用方法,都是常見的問題,多到數不清。

但總有人會以無辜的電腦用戶自居,向人問種種莫名的狀況,如果你回應他們說『不知道』,他們總是說:『怎麼會?你電腦不是很厲害,一定知道吧!?』。哪怕有人真有閒功夫,想盡辦法去查明過去所有的問題,也是惘然,因為在這時代,軟體每天都有更新改動,病毒每日都遵行達爾文理論。我們又哪裡有這麼多時間去一一了解呢?他們弄得好像我們如某漫畫裡的主角一般,什麼東西都會駕駛一樣。

『你會不會寫遊戲外掛?會不會寫某某遊戲?會不會自己寫一個某某軟體,然後就可以賺錢了。』

不難察覺講出這些詞的人,是完全不懂電腦方面技術的人,因此聽者多半是不太計較的。但是,他們其實有一定程度的汙辱人而不自知。為什麼這樣說?因為,會講出這些話的人,都把玩電腦很厲害和電腦技術很厲害劃為等號,絲毫無所尊重辛苦的求知者,在他們心理,只要是和電腦有關的人都是『玩電腦的』。

玩電腦很厲害只要多當孝子買東買西,多砸些時間看不明信件、安裝莫名軟體然後中毒、重灌系統就能達到一等一的水準;而真正電腦技術很厲害的人,不但要每天看書看文件,還要日夜鑽研實作各種技術、新想法,並想盡辦法吸收所有日新月異的資訊,廢寢忘食。這兩種人的能力和努力相差十萬八千里,辛苦更相差何止…

OrzMicrokernel irq_handlers.asm IRQ處理和操作系統計時器 IRQ0 的函式註解

irq_handlers.asm 是 Orz Microkernel 用來處理 IRQ 的函式檔案,裡面有著處理所有 IRQ 的程式碼。之前工作排程器的部份有提到,定時切換工作是靠 IRQ0 的系統計時器達成的,其原理是當工作被賦予執行權時,同時也被授權了能佔用 CPU 的時間長度,接著,再靠著系統計時器的功能倒數計時。最後,該工作能佔用 CPU 的時間被用完,系統計時器就會如同鬧鐘響起一般,去呼叫 tasks.asm 的 Schedule 準備切換到下一個工作行程。

程式碼和詳細註解:
;------------------------------------------------------------------------
; IRQ 是處理硬體中斷請求的通道,硬體可透過 IRQ 要求 CPU 產生中斷,以處理硬體的
; 相關操作。
;
; IRQ 在中斷(INT)的編號定義:
; 08 Hardware IRQ0 System Timer
; 09 Hardware IRQ1 Keyboard
; 0A Hardware IRQ2 Redirected
; 0B Hardware IRQ3 Serial Comms. COM2/COM4
; 0C Hardware IRQ4 Serial Comms. COM1/COM3
; 0D Hardware IRQ5 Reserved/Sound Card
; 0E Hardware IRQ6 Floppy Disk Controller
; 0F Hardware IRQ7 Parallel Comms.
; 70 Hardware IRQ8 Real Time Clock
; 71 Hardware IRQ9 Redirected IRQ2
; 72 Hardware IRQ10 Reserved
; 73 Hardware IRQ11 Reserved
; 74 Hardware IRQ12 PS/2 Mouse
; 75 Hardware IRQ13 Math's Co-Processor
; …

OrzMicrokernel tasks.asm 工作排程器的切換執行權函式註解

工作新增、載入後,一切都準備就緒了,main.asm 最後一個任務,就是讓作業系統的心臟:『工作排程器』開始跳動,使所有工作能夠生生不息的運轉。Orz Microkernel 利用了 Schedule 這個 Function 切換到工作清單中的下一個工作,並利用 IRQ_0 的計時器,每一定單位時間就切換一次,使所有工作如同同時執行一般。

原始程式碼和詳細註解:
;--------------------------------------------------------------------------
; Scheduler 工作排程器:
; 多工的實作原理是讓所有的工作行程,依序輪流切換佔用 CPU 的運算能力。
; 由於每個工作佔用 CPU 的時間極小,因此近乎同時所有工作同時執行一般。
;
; Schedule: 是用來切換到下一個工作,並讓其佔用 CPU 的函式。在 main.asm 中呼叫後,
; 就會設定 taskRunningTime 工作執行的時間長度,而在 irq_handlers.asm 中
; 的 IRQ_0 計時器,就會開始依照 taskRunningTime 定時轉換到下一個工作。

tx_NoMoreTasks db "No more tasks",0 ; 設定沒有工作的訊息字串

Schedule:
mov eax, [tasksCount] ; 設 EAX 為目前工作數量
and eax, eax ; 若工作數量為零則設定零值旗標 ZF = 1
jnz Schedule0 ; 若旗標 ZF = 0 則跳到 Schedule0

mov esi, tx_NoMoreTasks ; 在 ESI 寫入沒有工作的訊息字串
jmp GpFault ; 輸出訊息

Schedule0:
mov eax, [currentTaskN] ; 設 EAX = currentTaskN
inc eax …

簡易用C語言求方程式的根

最近有不少人向我求救,原因不外乎是學校的報告、作業,而其中最令人覺得有趣的,就是關於數值方法的程式撰寫的問題。而講到用電腦處理數學運算,大多數人的直覺一定是使用 MATLAB 來實作其程式。是的,課堂上所教所講的就是 MATLAB,在處理各種數學問題和方程式,MATLAB 都有完整的函式指令和功能,只要熟悉,可以很輕易的能夠達成各種數學任務。不過最好笑的是,教授雖然教的是 MATLAB,但考的卻是 C 和 Fortran,要求學生的報告、作業也都是 C 和 Fortran,這讓一群學生都傻了眼,不知道該怎麼辦。

先暫且不管課本中是否附有 MATLAB 的程式碼,對於一群只上過幾門程式語言課的人,仍然無法輕易將程式轉用 C 或 Fortran 來寫。像是這樣一個用C語言求方程式的根的程式,就是許多人的問題:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>

#define F(x) sin(10*x)+cos(3*x)

double f(double x) {
return F(x);
}

int sign(double x) {
if (x<0)
return -1;
else if (x>0)
return 1;
else
return 0;
}

void incsearch(double xmin, double xmax, int ns)
{
int i,n;
double space,current,ans;

current = xmin;
space = (xmax - xmin) / (ns-1);

for (i=0;i<ns-1;i++) {
if (sign(f(current))!=sign(f(current+space))) {
printf("%.4f\t%.4f\n", current, current+space);
}
current += space;
}
}

int main(int argc, char *argv[])
{
int n…

OrzMicrokernel tasks.asm 建立新工作函式註解

因為每天花在 Orz Microkernel 上的時間不多,而且這次的程式片斷比較長,所以讓我多花了幾天的時間做整理,直到現在才大約有個較完整的註解。繼續依順續閱讀 main.asm 中可以看到,在工作初始化多工機制和工作狀態的資料結構之後,就開始連續載入並新增 6 個工作到我們的工作清單中,main.asm 呼叫 tasks.asm 裡的 CreateTask 函式來新增工作。

以下是程式碼和詳細註解:
;--------------------------------------------------------------------------------------
; 建立新的工作 CreateTask
;;;; 輸入值: EAX = 工作的開始位址, EBX = 工作所佔的記憶體大小, ECX = 工作的堆疊大小
;;;; EDX = 工作的資料大小, ESI = 工作名稱指標
;;;; 返回值:若進位旗標 CF=1 則表示建立新工作失敗,否則 EAX = 工作編號
;;;; 會影響到的暫存器:EAX, EBX, ECX, EDX, ESI, EDI, EBP

CreateTask:
mov [taskNamePtr], esi ; 設 taskNamePtr 為工作的名稱
mov [taskBegin], eax ; 設 taskBegin 為工作的開始位址
mov [copySize], ebx ; 設 copySize 為工作所佔的記憶體大小
add ebx, ecx ; EBX 加工作的堆疊大小
add ebx, edx ; EBX 加工作的資料大小
mov [taskSize], ebx ; taskSize = 所佔記憶體 + 堆疊大小 + 資料大小

mov ebx, [tasksCount] ; 設定 EBX 為目前工…

Gmail 輸人不輸陣,免費空間快速爆增

這幾天瑣事繁多,令我閒不得半刻,所以有空的時間最多只能上網看看信件、檢查有無回應文章。不過就在這兩天登入信箱的時候,我發現 Google 開始有非常大的動作,那就是空間爆增了!截至這篇文章為止,我的信箱可用空間已達 3.7 GB!

回憶過去我選擇了 Gmail 來當我的私人信箱,就是因為它在操作介面上設計簡單輕便、且空間大﹝在 Gmail 剛推出 2GB 時其他免費信箱還只有5MB、10MB﹞。可是到了今天這時候,Hotmail 已經開始擴充到 5GB 的免費空間,Yahoo 也不斷有擴大空間的改進,令 Gmail 當初的空間已經不夠看了!可想而知, Gmail 的大容量吸引力就這樣開始慢慢消退。

但是,這幾天發現,我的信箱容量開始以每日約 200 MB 的速度增加,從2.9GB、3.1GB、3.3GB一直到今天的3.7GB,似乎還有繼續快速往上發展的趨勢,真是期待它會增加到什麼樣的地步。看來 Gmail 輸人不輸陣,似乎打算跟其他家服務商拼了。 :)

OrzMicrokernel tasks.asm 初始化多工機制和工作狀態的資料結構

main.asm 在設定完顯示模式後,緊接著就是開始初始化多工機制。我們將配置一塊記憶體來存放所有工作的名稱、執行狀態﹝包括各種暫存器的內容﹞,在 Orz Microkernel 中,工作數量被限制為 64 個,它被定義在 define.asm 裡頭。

程式碼和詳細註解:
InitTasks:
mov ecx, MAX_TASKS ; 設 ECX 為最大工作數量﹝定義在 define.asm﹞
mov ebp, tasks ; 設定工作狀態記錄所在位址﹝定義在tables.asm﹞
xor eax, eax ; 將 EAX 歸零

InitTasks0:
mov [ebp + s_task.taskID], eax ; 初始化工作狀態記錄編號
add ebp, s_task.msize ; 移到下一個工作狀態記錄的開頭
loop InitTasks0 ; 重覆工作狀態記錄初始化 ecx 次
ret


從上面程式碼看到的 s_task,是工作狀態記錄的資料結構,它被定義在 task.asm 的開頭:
; 使用虛擬指令 RESB、RESD 宣告不含初值的資料結構 s_task
struc s_task
.taskID: resd 1 ; 工作編號 Dword(2 Bytes)
.state: resb 1 ; 工作狀態 Dword(1 Byte)
.newTick: resd 1 ; need for state=STATE_SLEEP

.queue: resd (MAX_TASKS + 1)
.qFirst: resd 1
.qLast: resd 1

.s_taskID: resd 1
.s_message: resd 1
.s_dataPtr: resd 1
.s_dataSize: resd 1

.ta…

OrzMicrokernel video.asm 切換顯示模式的註解

依照 Orz Microkernel 在 main.asm 的核心啟動流程來看,在轉換到保護模式並做了最基本的初始化之後,就會 call SetVideoMode 來切換顯示模式到 320x200。關於這部份,比較複雜的是牽涉到 BIOS 中斷,由於在保護模式之下無法使用 BIOS INT,所以特別額外在 realmode.asm 寫了在保護模式下處理此類中斷的呼叫函式﹝關於 realmode.asm 的註解日後補上﹞。

程式碼和詳細註解:
;------------------------------------------------------------------------------
; 文字模式﹝80x25,16色﹞:AL=0
; 圖形模式﹝320x200,256色﹞:AL=1

SetVideoMode:
and al, al
jnz SetVideoMode0 ; 若 AL 不為 0 則呼叫 SetVideoMode0

mov [rmode_ax], WORD 0x03 ; 文字模式
mov al, 0x10 ; 中斷 10H

; 在保護模式下舊有的中斷向量表已被 IDT 代替
; 導致 BIOS 中斷在保護模式下無法使用
; 故 BIOS 中斷得在真實模式執行
; 因此特別在 realmode.asm 中設計一個處理真實模式中斷的函式
jmp RmodeInt ; 呼叫在 realmode.asm 的 RmodeInt 處理中斷

SetVideoMode0:
dec al ; AL=AL-1
jnz SetVideoMode1 ; 若 AL 不為 0 則呼叫 SetVideoMode1 返回

mov [rmode_ax], WORD 0x13 ; 圖形模式
mov al, 0x10 ; 中斷 10H
jmp RmodeInt ; 呼叫在 realmode.asm 的 Rm…

OrzMicrokernel bit32.asm 進入保護模式後的核心初始化

OrzMicrokernel 在 bit16.asm 完成了轉換成保護模式的工作,緊接著會跳到 bit32.asm 初始化核心和設定硬體中斷周期。最重要的是,從跳轉到這檔案後,就可以開始使用 32 位元的各種延伸暫存器以及更大的記憶體定址,但前題是:我們得先將新的暫存器區段位址設定好,因為暫存器設定還是處於在真實模式時的狀態。

程式碼和詳細註解:
[BITS 32]
; Intel 處理器的設計是以 dword 為單位來存取記憶體,所以記憶體位址定為 4 的倍數。
align 4 ; 設定所有資料的起始位址都是 4 的倍數
Begin32c:
; 初始化暫存器區段
mov ax, kernel_data - _GDT ; 從 tables.asm 的 kernel_data 開始
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x2FFFF ; 堆疊暫存器設在 2MB 記憶體後

call TestMemory ; 呼叫 mm.asm 的子程式測試記憶體

mov edi, memMap ; 初始化索引暫存器
; memMap 記憶體大小有0x20000=128KB
mov ecx, 0x100 / 32 ; 計數器設為 256/32=8
xor eax, eax ; 將 eax 歸零
dec eax
rep stosd ; 每次從 EAX 中複製 4 Bytes 到 EDI
; 複製 8 次

mov ecx, 0x20000 / 4 - (0x100 / 32) …

OrzMicrokernel bit16.asm 核心前置動作並轉換到保護模式的註解

bit16.asm 是 OrzMicrokernel 從真實模式轉換到保護模式的部份。保護模式是作業系統必要的運作環境,因為保護模式支援了 4GB 的實體記憶體定址、可擴充的記憶體分頁機制、多重任務機制、特權級等多工作業系統會用到的各種機制。

詳細程式碼和註解:
[BITS 16]
[ORG KERNEL_START]

Start:
; 初始化暫存器區段
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax ; 初始化堆疊區段
mov sp, 0x0000 ; 堆疊指標暫存器歸零

; 準備開始切換到保護模式
cli ; 禁用中斷以免干擾初始化
cld ; 設定字串處理時由低住址往高位址
call PrepareIRQs ; 呼叫 irqs.asm 的副程式以初始化 IRQ

; A20 位址線控制器:
; 早期 8086 的 CPU 只有 20 bits 的位址匯流排,只能定址到 1MB 的記憶體
; 因此,如果試圖存取超過 1MB 的記憶體,將會回到位址零重新定位循環下去
; 所以,要打開 A20 位址線,讓電腦可以定址大於 1MB 的記憶體
call A20En ; 呼叫 irqs.asm 的副程式
; 以打開 A20 位址線

lgdt [_GDT - KERNEL_START] ; 載入 GDT:_GDT 在 tables.asm
lidt [_IDTR - KERNEL_START] ; 載入 IDT:_IDTR 在 tables.asm

; 切換到保護模式
; 切換成保護模式的方法:將 CR0 的 PE 欄位(Bit0)設為 1
;
; 控制暫存器 CR0…

OrzMicrokernel boot.asm 啟動磁區定義和程式初始化註解

OrzMicrokernel 的 boot.asm 是針對 1.44 MB 磁碟片所設計的,採用的是 FAT12 ﹝注意不是 FAT16 或 FAT32﹞的檔案系統,所以,在啟動磁區的部份的定義上比較簡單且單純。另外,這次註解的內容主要是程式一開始的部份,包括初始化、在螢幕上顯示一段文字等。

程式碼與詳細註解:
Base:
jmp short Begin ; 從 Begin 執行
nop ; 空執行

; 啟動磁區格式
oem db 'OrzMicro' ; 廠商軟體名
bytesPerSector dw 0x0200 ; 設每磁區的大小為 512 Bytes
sectPerCluster db 0x02 ; 每磁叢的磁區數
resSectors dw 0x01 ; FAT 前的磁區數
; ﹝開機記錄所佔用的磁區數﹞
fatCopies db 0x02 ; FAT 表總數
rootEntCnt dw 0x0070 ; 根目錄的最大檔案數
totalSectors dw 0x05A0 ; 邏輯磁區總數
media db 0xF9 ; 磁碟種類
sectPerFat dw 0x0003 ; 每個 FAT 表的磁區數
sectPerTrack dw 0x0009 ; 每磁軌的磁區數
heads dw 0x0002 ; 磁頭數﹝面數﹞
hiddenSectors dd 0 ; 隱藏磁區數
sectorHuge dd 0 ; 保留 4 Bytes
drive db …

OrzMicrokernel boot.asm 磁碟內容參數的詳細註解

這是 OrzMicrokernel 裡 boot.asm 磁碟內容參數的部分,這部份比較直觀且不涉及硬體裝置的操作和定義,只要了解磁碟的資料配置結構,很容易就能了解其中的運作。

程式碼包括詳細註解:
;-----------------------------------------------
; 計算各項磁碟內容參數
;
; 計算磁區位置:
; 1. FAT表磁區
; 2. 根目錄磁區
; 3. 資料磁區
;
; 計算 FAT 檔案系統資訊:
; 1. FBD 所佔磁區數量
;-----------------------------------------------
;
; 磁碟資料結構:[開機記錄磁區][隱藏磁區][FAT表磁區][根目錄磁區][資料磁區]
; [FAT表磁區] 是由許多 FAT 表所構成
; [根目錄磁區] 是由許多 FBD 所構成
; FBD(檔案描述區塊):大小為 32Bytes
; 每個檔案都有自己的 FBD,記錄檔案名稱、建檔日期、檔案大小等資料

; 計算 FAT 的開始位置
; 方法:跳過隱藏磁區和開機磁區
; hiddenSectors = [高位元 2Bytes][低位元 2Bytes]
mov si,[hiddenSectors] ; 低位元給 SI
mov di,[hiddenSectors+2] ; 高位元給 DI
add si,[resSectors] ; 跳過隱藏磁區加開機記錄所佔用的磁區數
adc di,0 ; DI:SI = 第一個 FAT 表的開始位置
mov [fatStart],si ; 用 fatStart 保存 FAT 的開始位置
mov [fatStart+2],di

; 計算根目錄磁區的開始位置
; 方法:跳過 FAT 表磁區
; FAT 表的總數 x 每個表的磁區數 = 所有 FAT 表的磁區總數
mov al,[fatCopies] ; AL = FAT 表總數
xor ah,ah …

OrzMicrokernel 陪我度過颱風天的阿宅生活

最近幾次少數能以『強烈的颱風』身份影響並登陸台灣的,大概就屬這次的颱風了。還沒登陸,風就已經重擊了家裡的窗戶,呼呼砰砰聲搞得我醒著做事覺得煩、睡也睡不好。前兩天晚上去逛敦南誠品,在去的路上,風狂野的在短短一個街口,就把我的雨傘給吹成奇形怪狀﹝比翻過來還可怕﹞,後來回家時只好淋著雨一身濕答答的走回停車處。在被風玩弄之後,我就決定這兩天不出門了,弄得自己閒得發荒,一直在家當阿宅 Orz。

我突然想起前陣子 Jserv 寫的 OrzMicrokernel,一直沒能好好的找個時間去讀它,所以,藉著這次颱風過境,就好好品嘗一下了﹝笑﹞。一切果然如同大家所說, OrzMicrokernel 真的是簡潔有力,是一個作業系統設計的優良範例,它的設計非常直觀易懂;可是我也有聽到一群朋友,看得要死還是看不大懂 OrzMicrokernel 。搞半天,原來他們對組合語言並不是說非常熟悉,還有對磁碟 LBA、CHS、FAT12 等等這種格式定義很陌生!不過也難怪,大多人平時寫程式都太高階了,早就忘了電腦是怎麼動起來的。

尋思如果很多想了解的人,都卡在對組合語言的一點不熟悉和資料結構定義這種問題上,導致無法踏入 OrzMicrokernel 的話,那真的是太可惜了。所以,無聊到極點的我,就決定開始幫 OrzMicrokernel 逐行中文註解,希望從組合語言指令用法開始加註說明,還有說明重要的資料結構﹝註解寫起來比程式碼還多行,驚!﹞。為了說明的完全清晰正確,我還重新查資料驗證自己的觀念,生怕有一點遺漏。

以下是 boot.asm 讀取磁區的註解片段:

; DX:AX = LBA 磁區位置, ES:BX = 緩衝區位址
ReadSector:
; 磁區 CHS 表示法
; 磁碟和磁片的記錄方式『不是』一整面的磁區用完再換面儲存,而是以空心圓柱狀的方式。
; 因為磁片只有簡單兩面,以磁片為例:
; 正確的磁片資料記錄順序為:第零面第零軌、第一面第零軌、第零面第一軌、第一面第一軌...
; 每面的同一軌都用滿後才換下一個磁軌紀錄資料,形狀如同空心圓柱,故稱磁柱(cylinder)。
; 因此以磁片來說,第 0 面第 0 軌和第 1 面第 0 軌就是第 0 磁柱

; 磁區 LBA 表示法
; LBA 編號法是不考慮磁區的實體 CHS 上,純粹將所有磁區統一由 0, 1, 2...編號到最後一個磁區

; …