2008年2月24日 星期日

Multi-dimensional Logic Computing Model 多維邏輯運算模型的廣義構想

Standard
為了弄出一些空硬碟,我整理一些舊資料想燒出來,無意間發現過去的一些有趣想法〔笑〕。這是我高中時期的一個構想,原本是針對思考現今程式邏輯的不足,所提出的另一種邏輯原型。當時純粹只是為了解決線性邏輯的單方向性,所以僅僅在解決新模型的結構問題。過程以簡單的幾何方式來緒述初步架構,並未思考到實際的應用,當然也缺少許多實際的數學推導。但是由於長期接觸資料庫的應用和系統的規劃,後來在高三時的一些空閒時間,有嘗試運用和簡化該模型使用在資料庫處裡的系統上,雖有些初步成果,但也還不到實用階段。

簡單來說,對邏輯要就是 off(false) 不然就是 on(true),這是 0 與 1 的基本概念。但進一步的邏輯是線性的, 0 與 1 決定了兩條向前走的路,如果每碰到一個 0 與 1 的判斷算是一個 Node ,那每個 Node 連起來最終會形成一條直線,且這條直線上有一個 Node 會是我們要的答案,至於是哪一個呢?就是由程式設計師決定了。線性邏輯已經被全世界的數學家、哲學家、科學家和 Hacker 發揮到淋離盡緻,它的確可以稱為今日的科技之母,當然,Multi-dimensional Logic Computing Model 也是架構在這上面。

只不過線性邏輯總是無法處理意外(exception) 的狀況,解決兩個選擇以外的組合,需要靠更多的 Node 去解決。我們必需先用人腦判斷是否有意外發生,再去針對意外設計 Node 解決,這也是為什麼當今 Artificial Intelligence 遲遲無重大突破的原因。雖然出現很多模糊理論、基因演算法、神經網路理論,但也不過是想辦法將 exception 當成訓練因子,歸納收斂成一個運算的方向,再用固定形式去求出答案。

在這種狀況下,線性邏輯顯然不夠用。從幾何的角度切入,一直線可以有無盡的 Node;從數學方程式來看,若是等號兩邊相等,一條線最終的 Node 必等於第一個 Node。從此假設可以發現,邏輯線被繞成一個圈,但是在應用面來說,這個圈沒有答案、沒有意義。為了解決這問題,於是想到種方法,若使用兩條邏輯線,必會產生兩個互相交集的點,這也代表答案將可能出現在這兩個點上面〔也就是停住有解答〕。

現在假設邏輯線可能很多且不論是否有交集:若每條線相同長度,無窮的圓周線將有可能變成一個球體狀,但事實上不同的邏輯不可能同樣長,這造成了形狀應該是一個類似太陽系的樣貌,上面有無窮個不同的行星軌道繞著太陽在轉。

回到真實狀況,如果有一條邏輯線很長,但它可能連續兩個 Node 與另一條線有交集,那麼依照目前的結構,顯然是不可能的。原因是:

  1. 每條邏輯線皆圍繞相同圓心
  2. 相同長度的邏輯線才可能相交
  3. 若交集,必有兩點相交或完全相同

用人類真實狀況舉例,你可能因為 A 事影響你下一步的 B 想法,這 A 可能有好幾種 factors 會影響 B ,也可能完全會影響 B 的流程,若依照現在的模型是無法詮釋的。

解決辦法,就是令邏輯線不規則,且不一定要有同樣的圓心。此時整個模形遠看就像一個有凹凹凸凸表面的月球一般,每個 Node 都有可能與其它線上的 Nodes 自成一條新的 Line。如此,錯縱複雜的邏輯線,大致上應該能表達各種邏輯運算,只是有個問題,我們該如何駕御它?〔畢竟決定無窮條邏輯線的起源是不可能的〕

這問題也令我思考很久,不能拿來應用的模型等於『廢物』一樣〔這是 jserv 常說的 :P〕!後來在高三的時候,就想出種應用的方式,可以用在資料庫的搜尋系統上。當時我暫時為這方法取名為『表面法』,如同字面意義,從表面來建構這邏輯的片面。

這方法是以五邊形為單位,中心有一個 Node,且被 5 Sub-Node 所圍繞,但 Sub-Node 同時也是中心,也有被其它 Sub-Node 所圍繞,用這方式可確保任何不規則的邏輯表面能被詮釋,在過去畫的 Freemind 心智圖中有清楚的表示。



圖中有分別說明 Node 和 Node 間的關係、 Search 時的 Rules、Input 的 Rules,還有當某個 Node 毀掉、狀況不明確時的自我細胞和資訊修復,其中的 Cache 設計更是完備整個流程的關鍵〔不只是加速而已〕。用在 Database Cluster 上,應該能有無比的速度才是〔大概吧 :P〕!最重要是有機會完成精準的圖形、聲音的辨識系統。由於 Node 和 Node 之間的互動會自成新的邏輯線,如果再加上無限迴圈、突變 Node 等設計,應該也能讓程式自我成長發展〔也就是程式可隨時自己改寫自己每一段邏輯〕。

不過相較於人腦每秒數以億計的腦細胞同時在跑,當今的 CPU 在單位時間內跑多少 Node 呢?

後記

惡搞一通還要為這想法弄個應用方法,我想我真的是瘋掉了。

2008年2月22日 星期五

OS Kernel 相關閱讀與實作心得﹝一﹞

Standard
極慚愧的是,這次寒假的計劃並未完全如期完成,實作 OS Kernel 的計劃只進行到 IRQ 的部份。不過,自從最近拜讀《即時多工核心程式設計》之後,深深被其所影響,讓我想好好重新調整實作的步調並整理針對 OS Kernel 設計的心得。

這本大作將核心架構在 DOS 環境上,用 DOS 省去了讀者摸索低階的時間,並以 C++ 的邏輯表達和 Template 的優勢加上必要的 ASM coding 設計一個 pure 的即時多工核心。尤其以直接切入 Kernel 運作為主題的方式,讓讀者更能立即擁有享受 Hacking Kernel 的成就感。

一個多工 OS Kernel 應該具備的機制,大致如下:
  1. Task Context Switching/Scheduler
  2. IPC (Inter Process Communication)
  3. Semaphore

事實上, OS Kernel 基礎的結構本來就並不龐大、複雜,這與『 microkernel 』的定義指標極為相似。而且,只存在該有的 Task Handler 和各工作 Connection 的機制,也容易理解與設計。至於其它的 Hardware Manager 等功能,其實並不需要在第一時間就去規劃,依據需求再決定於 Kernel Mode 或 User Mode 之下實作就可以了。


【Task Context Switching/Scheduler】

除了 Context Switching 會無可避免的消耗 CPU 資源之外,在 Task 控制的規劃上,有著兩種模式,可視 OS 用途而定:
  1. Preemptive
  2. Non-preemptive

Preemptive 即為強佔式〔強取式〕核心,在 Scheduler 被觸發時,優先權高的 Task 將會搶去優先權低的排隊位置,取得先執行的權力。此方式的系統回應快,對於 Realtime OS 是非常必要的技術。Linux 在 2.6 版後就加入了這項技術,以提升需要 Realtime 的多媒體效能。

Non-preemptive 則是無論如何,每個 Task 用完自己的時間後交出使用權利,照順序一個個 Task 執行,對於優先權高的 Task 來說也不能不遵守此規則,要等到自己的時間到來才可以執行,故可能造成 Task 無法即時回應使用者的需求。此種核心適合需平穩執行與資源平均分配的系統,如 Server 用途就屬此類。

至於工作排程器(Task Scheduler) 的設計細節包含兩種方法:
  1. Priority Scheduling
  2. Round-robin Scheduling/Time Slicing

Priority Scheduling 方法是依照 Task Priority 的大小為順序,依序切換執行 Task。

而為避免 Task 的執行時間過長,就出現了 Round-robin Scheduling/Time Slicing 方法,使用時間切割的方式,讓每個 Task 執行一段時間就交出執行權,如此便可實現 Tasks 同時執行的多工錯覺。當然,也可更進一步控制執行時間片斷的長短,使優先權較高的 Task 有較充裕的時間可使用。

關於更多 Sheduler 的實作,在之前提到過的 [OrzMicroKernel task.asm] 可以找到一些簡潔又基本的設計。


【IPC (Inter Process Communication)】

在多工的系統中,Tasks 有如同時在執行一般,所以有很多機會協同工作。由於 Task 之間會互相傳遞資訊,需要 IPC 機制來達成這任務,IPC 通常分為兩個部份:
  1. Message Pipe (不具名的管道)
  2. Message Port (具名的管道)

Message Pipe 是一個不需要具名的通訊管道,將資料排隊存在一個『環狀緩充佇列』,然後接收端會以 FIFO(先進先出) 的方式讀出。而 Message Port 是一個需要指定收件人的通訊管道,有如寄信件一般。


【Semaphore】

同樣也是 Task/Thread 在做協同工作時必要的功能,Semaphore 使資源能夠共享而不打架。

Semaphore 的實作是建立一個計數器開關,預設值賦予 1。計數器的值大於 0 代表無人使用和等待,小於等於 0 則代表有人在等待資源。每當 Task/Thread 求資源時,先減一然後檢查計數器的值,若大於等於 0 就取用資源,若是小於 0 則進入等待區;用完資源後,則將計數器加一並叫醒下一個正在等待區排隊的程式。

用 C 語言表示其邏輯,大致像這樣:

/* 取用 semaphore */
sem--;
if (sem<0)
pend(); /* 進入等待區 */

/* 釋放 semaphore */
sem++;
if (sem<=0)
post(); /* 叫醒下一個正在等待的程式 */



後記

過去總認為,一個 Kernel 就是要從 Booting 開始著手,一步步解決 A20 、 protect mode 、IRQ、Memory 等低階的問題,再開始實作 Task Sheduler 等更進階的部份。事實上,由於低階的部份較不直覺,通常會花不少時間在研究其原理,因此在實用機制上的設計,反到是從未注重過。這也或許是市面上的書籍和 Open Source Community 對於 Coding OS Kernel 都強調從頭開始實作所致,讓從零開始的章節反而成為是實作過程的重點。

2008年2月21日 星期四

經典著作《即時多工核心程式設計》

Standard
日前 Jserv 康慨借我一本經典著作《即時多工核心程式設計》,於是在寒假最後的空閒時間先快速看過了一遍。真不愧是 Jserv 大力推薦的好書,作者將多工核心的藍圖具體而詳盡的勾勒出來,短短數個章節已將重要細節交代非常清楚,由於該書的範例說明用 C++ 來撰寫表達,結構與觀念也都非常易懂,重複閱讀了幾遍也能品嘗出不同細節的奧妙之處。這本經典著作的模樣可以在 Jserv's blog 找到:

http://blog.linux.org.tw/~jserv/archives/001258.html

該書於 1995 年出版,所附 Floppy 早就發霉爛掉了,不過有 Jserv 大神的筆記和筆跡,想必比那片張 Floppy 更值不少錢吧!〔笑〕

Jserv 說這本書在某些書店架上還可以看得到,有興趣的人可以去找找看。

2008年2月17日 星期日

GTK+ 馬戲團:邪惡的 g_signal_connect 出場

Standard
GTK+ 馬戲團有五花八門的精彩表演,而今天壓軸好戲就是『邪惡的野獸 - g_signal_connect 』。g_signal_connect 可以戰勝所有的 widget 和 window,有強而有力的爪子,能劃破層層阻礙;有尖銳嚇人的牙齒,只須咬一口便深可見骨。雖然它已被馴服,但隨時還是有機會獸性大發反撲過來。:P

GTK+ Program 本身並不難,就算是第一次接觸的人,也能輕易上手並寫出什麼東西來。但對於較常接觸 GTK+ programing 的人,卻常會抱怨 GTK+ 的設計,層層的 window 和 widget 常搞到什麼事都要兜個大圈才能解決。而對 GTK+ 設計架構和細節了解不夠透徹的人,更是常常寫 5 分鐘 code ,卻要花 5 個小時 debug ,小弟我就是最好的例子 :(

所以,探討 GTK+ 的一些細節,有助於我們在撰寫 GTK+ 程式時避免問題的發生,減少 debug 的時間。

接觸過的人都曉得, GTK+ 的優點就是將各種東西都寫成一個個直接的 function call,任何人都可以很容易的去呼叫使用。但那些 function call 的背後常常各自為政互相打架,卡來卡去的結果不是『無限迴圈』就是『程式流程大亂』,我們如果不深入了解 GTK+ 的運作,這些問題將都會無解。

在 GTK+ 之中很多問題的發生,都是從邪惡的 g_signal_connect 開始。它是 GTK+ 各種物件的共通管道,programer 們可以利用它設定各種狀況的 handler,像是 widget 的 button-press-event, toggled ...etc。不過 g_signal_connect 所決定好的事,卻是有各種不同的觸發時機,有些會馬上就觸發如: toggled 事件,有些會在所有 event 處理完後,idle 時才觸發。若是沒有掌握好觸發的時機,一切的流程設計將會毀滅,而且找不到問題所在。

但是這些不同的時機,要深入了解 GTK+ Source 之後才會發現。對於一般 programer 來說,可沒這麼多時間一一深入了解每一個元件的運作,最好的解決辦法就是養成習慣,所有 widget 和 window 都設定好 Ready 之後,再呼叫 g_signal_connect 設定 event handler,這樣可以避免掉很多問題發生。

當然還有其他 g_signal_connect 衍生的問題,不過不再多提,有機會再整理成文好了。 :P

2008年2月16日 星期六

解決 Atheros AR5007EG driver 和 bus numbers 不正確的問題

Standard
買新的 Laptop 本該是快樂的,但為了讓新電腦能夠好好的跑 Linux ,卻讓我睡不好覺好幾天,一點快樂的心情都沒有!

在抗戰了整整一個星期,挖遍了 madwifi.org 每個角落,請 google 大神解答了無數個問題後,終於成功的使我新買 Laptop -『 Toshiba Satellite L40 PSL48T-00K00J 』的 Wireless 動起來了!宣告戰勝的心情真是難以言喻! :P

扣除掉 Wireless,這台機器對 Linux 的支援度非常好,所有 Device 都能很順利的跑起來。由於顯示晶片是 Intel 965 ,Compiz 3D Desktop 不必特別設定就能順利的運作,3D 特效的效能也相當令人滿意﹝不能跑3D桌面我就不買了﹞。最重要的是整台機器只需要 2 萬元不到,實在是非常划算,對於預算不高的我,可以不必花大錢就足以應付 coding 、 presentation 、 炫麗的視覺效果等需求,有一分錢都沒浪費的感覺。﹝有些電腦就不知道在貴什麼了,令人想不透!﹞

因為目前的 Linux 並不支援這機器的 Wireless chipset -『 Atheros AR5007EG 』(與 EeePC 同系列的晶片),必須使用 Atheros 公司 patch 後的 madwifi driver 才能驅動 Wireless,這是比較麻煩的地方。﹝該 patch 並未 opensource 給 madwifi team,導致 madwifi team 不能接受此 patch,因此近期要看見 madwifi 正式支援 AR5007EG,恐怕是很困難的一件事﹞

關於該 patch 的取得和其詳細討論可在這找到:

http://madwifi.org/ticket/1679


But, 別高興太早,如果是這麼簡單,我也不必奮戰一整個星期了。

如果你的 NB 在 patch driver 後就能使 AR5007EG chipset 正常啟動,非常恭喜你。但是,如果你跟我一樣是
『 Toshiba Satellite L40 PSL48T-00K00J 』這台機器,你應該會發現你的 wireless 還是不會動才對。:(

原本以為是驅動程式的問題,但經過多天的更換 madwifi driver 或使用 ndiswrapper 都無法搞定,甚至還瘋狂的將 madwifi 的 HAL 整個反組譯看個究竟。直到最後要放棄時才發現,某些 Laptop 的 BIOS 會令 kernel 無法正確指定『 PCI/cardbus bridge secondary/subordinate bus number 』,所以 kernel 無法控制位於那些 bridge 上的裝置。要解決這問題,你可以在電腦啟動時加入 "pci=assign-busses" boot option。

經查過一些資料後,發現此 option 是在 Linux kernel 2.6.13.3 時被加入的,其原 kernel patch 說明如下:

In some cases, especially on modern laptops with a lot of PCI and
cardbus bridges, we're unable to assign correct secondary/subordinate
bus numbers to all cardbus bridges due to BIOS limitations unless
we are using "pci=assign-busses" boot option.
So some cardbus controllers may not have attached subordinate pci_bus
structure, and yenta driver must cope with it - just ignore such cardbus
bridges.


加入 boot option 再重新開機後,我新買的 Toshiba Satellite L40 PSL48T-00K00J 就可以順利的連接無線網路了。﹝笑﹞

註:由於
assign bus number 的問題可能發生在任何裝置上,要是你確定 NB 上的 Sound、Ethernet 等晶片或裝置 Linux 都有支援,可是卻無法正確運作,也請試試用本文的方法解決看看。

後記

Wireless 不動,誰也想
到是 bus number 錯誤惹的禍。這種底層 IO 通道指定錯誤的問題,雖曾在過去 Window 95/98 年代發生過,但今天恐怕不會有什麼人會相信它再次發生了,尤其還是發生在『企鵝』身上。

另外,多謝『 Kiss of Death 』這部老電影,提醒了我該注意更底層會忽略的事,臥底往往比看得見的敵人來得有威脅性多了,時時謹慎對他人搜身可以保證『邪惡』的事不被曝光洩露。看完這部電影,當下我就決定從 BIOS 和 kernel 的部份著手,沒想到不到半小時就搞定了一個星期無法解決的問題 XD

2008年2月9日 星期六

實作 Linux Kernel-space 的 UDP Server

Standard
今天在『Linux Kernel Newbies』閒逛,看近期 Kernel 的動態和變化,心中不時贊歎 Linux 發展的複雜和快速,每次去都令人驚奇。該網站上除了有 kernel 的最新資訊,也有一些相關的文件和教學手冊,而其中一篇文獻『Simple UDP Server in Kernel-space』簡單示範了 kernel thread 的使用以及 Module 的撰寫,相當值得推薦參考。

由於完整程式碼不長,在這裡摘錄:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

#include <linux/kthread.h>

#include <linux/errno.h>
#include <linux/types.h>

#include <linux/netdevice.h>
#include <linux/ip.h>

#include <linux/in.h>

#include <linux/delay.h>

#define DEFAULT_PORT 2325
#define CONNECT_PORT 23
#define MODULE_NAME "ksocket"
//#
define INADDR_SEND ((unsigned long int)0x7f000001) /* 127.0.0.1 */

#define INADDR_SEND INADDR_LOOPBACK

/*
2006/06/27 - Added ksocket_send, so, after receive a packet, the kernel send another back to the CONNECT_PORT
- Rodrigo Rubira Branco <rodrigo@kernelhacking.com>

2006/05/14 - Initial version
- Toni Garcia-Navarro <topi@phreaker.net>
*/


struct
kthread_t

{
struct
task_struct *thread;
struct
socket *sock;

struct
sockaddr_in addr;
struct
socket *sock_send;

struct
sockaddr_in addr_send;
int
running;
};

struct
kthread_t *kthread = NULL;

/* function prototypes */
int
ksocket_receive(struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len);

int
ksocket_send(struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len);

static void
ksocket_start(void)
{
int
size, err;

int
bufsize = 10;
unsigned char
buf[bufsize+1];

/* kernel thread initialization */
lock_kernel();
kthread->running = 1;

current->flags |= PF_NOFREEZE;

/* daemonize (take care with signals, after daemonize() they are disabled) */
daemonize(MODULE_NAME);

allow_signal(SIGKILL);
unlock_kernel();

/* create a socket */

if
( ( (err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &kthread->sock)) < 0) ||

( (err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &kthread->sock_send)) < 0 ))

{
printk(KERN_INFO MODULE_NAME": Could not create a datagram socket, error = %d\n", -ENXIO);

goto
out;
}

memset(&kthread->addr, 0, sizeof(struct sockaddr));

memset(&kthread->addr_send, 0, sizeof(struct sockaddr));

kthread->addr.sin_family = AF_INET;
kthread->addr_send.sin_family = AF_INET;

kthread->addr.sin_addr.s_addr = htonl(INADDR_ANY);

kthread->addr_send.sin_addr.s_addr = htonl(INADDR_SEND);

kthread->addr.sin_port = htons(DEFAULT_PORT);
kthread->addr_send.sin_port = htons(CONNECT_PORT);

if
( ( (err = kthread->sock->ops->bind(kthread->sock, (struct sockaddr *)&kthread->addr, sizeof(struct sockaddr) ) ) < 0) ||

(err = kthread->sock_send->ops->connect(kthread->sock_send, (struct sockaddr *)&kthread->addr_send, sizeof(struct sockaddr), 0) < 0 ))

{
printk(KERN_INFO MODULE_NAME": Could not bind or connect to socket, error = %d\n", -err);

goto
close_and_out;
}

printk(KERN_INFO MODULE_NAME": listening on port %d\n", DEFAULT_PORT);

/* main loop */
for
(;;)
{
memset(&buf, 0, bufsize+1);

size = ksocket_receive(kthread->sock, &kthread->addr, buf, bufsize);

if
(signal_pending(current))
break
;

if
(size < 0)

printk(KERN_INFO MODULE_NAME": error getting datagram, sock_recvmsg error = %d\n", size);
else
{

printk(KERN_INFO MODULE_NAME": received %d bytes\n", size);
/* data processing */
printk("\n data: %s\n", buf);

/* sending */
memset(&buf, 0, bufsize+1);

strcat(buf, "testing...");
ksocket_send(kthread->sock_send, &kthread->addr_send, buf, strlen(buf));

}
}

close_and_out:
sock_release(kthread->sock);

sock_release(kthread->sock_send);
kthread->sock = NULL;

kthread->sock_send = NULL;

out:
kthread->thread = NULL;

kthread->running = 0;
}

int
ksocket_send(struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len)

{
struct
msghdr msg;
struct
iovec iov;

mm_segment_t oldfs;
int
size = 0;

if
(sock->sk==NULL)
return
0;

iov.iov_base = buf;
iov.iov_len = len;

msg.msg_flags = 0;
msg.msg_name = addr;

msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_control = NULL;

msg.msg_controllen = 0;
msg.msg_iov = &iov;

msg.msg_iovlen = 1;
msg.msg_control = NULL;

oldfs = get_fs();
set_fs(KERNEL_DS);

size = sock_sendmsg(sock,&msg,len);
set_fs(oldfs);

return
size;
}

int
ksocket_receive(struct socket* sock, struct sockaddr_in* addr, unsigned char* buf, int len)

{
struct
msghdr msg;
struct
iovec iov;

mm_segment_t oldfs;
int
size = 0;

if
(sock->sk==NULL) return 0;

iov.iov_base = buf;
iov.iov_len = len;

msg.msg_flags = 0;
msg.msg_name = addr;

msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_control = NULL;

msg.msg_controllen = 0;
msg.msg_iov = &iov;

msg.msg_iovlen = 1;
msg.msg_control = NULL;

oldfs = get_fs();
set_fs(KERNEL_DS);

size = sock_recvmsg(sock,&msg,len,msg.msg_flags);

set_fs(oldfs);

return
size;
}

int
__init ksocket_init(void)
{
kthread = kmalloc(sizeof(struct kthread_t), GFP_KERNEL);

memset(kthread, 0, sizeof(struct kthread_t));

/* start kernel thread */
kthread->thread = kthread_run((void *)ksocket_start, NULL, MODULE_NAME);

if
(IS_ERR(kthread->thread))
{
printk(KERN_INFO MODULE_NAME": unable to start kernel thread\n");

kfree(kthread);
kthread = NULL;
return
-ENOMEM;

}

return
0;
}

void
__exit ksocket_exit(void)

{
int
err;

if
(kthread->thread==NULL)

printk(KERN_INFO MODULE_NAME": no kernel thread to kill\n");
else
{
lock_kernel();

err = kill_proc(kthread->thread->pid, SIGKILL, 1);

unlock_kernel();

/* wait for kernel thread to die */
if
(err < 0)

printk(KERN_INFO MODULE_NAME": unknown error %d while trying to terminate kernel thread\n",-err);
else
{

while
(kthread->running == 1)
msleep(10);

printk(KERN_INFO MODULE_NAME": succesfully killed kernel thread!\n");
}
}

/* free allocated resources before exit */
if
(kthread->sock != NULL)
{

sock_release(kthread->sock);
kthread->sock = NULL;

}

kfree(kthread);
kthread = NULL;

printk(KERN_INFO MODULE_NAME": module unloaded\n");
}

/* init and cleanup functions */

module_init(ksocket_init);
module_exit(ksocket_exit);

/* module information */
MODULE_DESCRIPTION("kernel thread listening on a UDP socket (code example)");

MODULE_AUTHOR("Toni Garcia-Navarro <topi@phreaker.net>");
MODULE_LICENSE("GPL");


如果以上程式碼有什麼遺漏,敬請見諒,一切還是以原文為準。另外,該網站除了 UDP Server 之外,還有 Web Server in kernel-space 的範例,有興趣的人可以自行去閱讀研究:

http://br.kernelnewbies.org/docs/modulos_guia.html#threads