2007年9月24日 星期一

親手打造 HTTP 網路服務:超小 Web Server 的撰寫

Standard
今天一時心血來潮下,前後花了 30 分鐘,動手寫了一個 Web Server,這是個僅僅使用 170 行﹝若是移除空行和註解也許只有 100 行不到﹞左右 C 語言程式所寫的『超小』Web Server,只有支援 GET 命令,沒有 CGI 、虛擬目錄的其他功能。

以下是瀏覽器對伺服器一個單純的 HTTP 通訊:

GET /index.html HTTP/1.0<CF><LF>
<CF><LF>


HTTP 協定中,標準網路通訊 Port 是 80,瀏覽器使用『GET /index.html HTTP/1.0』命令來指定抓取網站根目錄的 index.html 檔案,每行指令以換行字元『\r\n』結尾,最後再一次的『\r\n』代表等待伺服器回傳資料。就這段非常單純的通訊內容而言,我寫的 Web Server,最少要能夠解析這行命令。


撰寫 Web Server 比較需要的程式技術,大概就是 Daemon、多執行緒、網路連線操作。


關於背景服務的 Daemon 程式寫法,我在另一篇筆記上有簡單記錄,應該不是什麼大問題。

而多執行緒的使用,是為了提供多使用者同時連線,我們需要同時處理眾多用戶端的連線要求,而不是等一個用戶處理完再接受下一個人連線。但是,為了不使用太複雜的多執行緒系統機制,在這程式中我改用 fork() 多行程的方式撰寫,這樣不但簡化了程式,也剛剛好讓我的 Web Server 可以在許多嵌入式系統中執行,因為多數微型的嵌入式系統,並沒有支援多執行緒。

最後一個會用到的程式技術是網路連線操作,會和寫用戶端網路程式有極大不同,因為要監聽網路卡上的 Port,等到有用戶端連線時再進行處理。其中依照流程會呼叫下面四個函式:

socket() /* 開啟網路 Socket */
bind() /* 開啟網路監聽器 */
listen() /* 開始監聽網路 */
accept() /* 等待客戶端連線 */


這 Web Server 將會使用 /tmp 當根目錄,每次有使用者連線,就會 fork() 出一個子行程去處理用戶端的要求。主要由 main() 啟動和初始化網路監聽、fork(),然後交由自訂的 handle_socket() 去解析命令和回傳資料。

Web Server 原始碼:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFSIZE 8096

struct {
char *ext;
char *filetype;
} extensions [] = {
{"gif", "image/gif" },
{"jpg", "image/jpeg"},
{"jpeg","image/jpeg"},
{"png", "image/png" },
{"zip", "image/zip" },
{"gz", "image/gz" },
{"tar", "image/tar" },
{"htm", "text/html" },
{"html","text/html" },
{"exe","text/plain" },
{0,0} };

void handle_socket(int fd)
{
int j, file_fd, buflen, len;
long i, ret;
char * fstr;
static char buffer[BUFSIZE+1];

ret = read(fd,buffer,BUFSIZE); /* 讀取瀏覽器要求 */
if (ret==0||ret==-1) {
/* 網路連線有問題,所以結束行程 */
exit(3);
}

/* 程式技巧:在讀取到的字串結尾補空字元,方便後續程式判斷結尾 */
if (ret>0&&ret<BUFSIZE)
buffer[ret] = 0;
else
buffer[0] = 0;

/* 移除換行字元 */
for (i=0;i<ret;i++)
if (buffer[i]=='\r'||buffer[i]=='\n')
buffer[i] = 0;

/* 只接受 GET 命令要求 */
if (strncmp(buffer,"GET ",4)&&strncmp(buffer,"get ",4))
exit(3);

/* 我們要把 GET /index.html HTTP/1.0 後面的 HTTP/1.0 用空字元隔開 */
for(i=4;i<BUFSIZE;i++) {
if(buffer[i] == ' ') {
buffer[i] = 0;
break;
}
}

/* 檔掉回上層目錄的路徑『..』 */
for (j=0;j<i-1;j++)
if (buffer[j]=='.'&&buffer[j+1]=='.')
exit(3);

/* 當客戶端要求根目錄時讀取 index.html */
if (!strncmp(&buffer[0],"GET /\0",6)||!strncmp(&buffer[0],"get /\0",6) )
strcpy(buffer,"GET /index.html\0");

/* 檢查客戶端所要求的檔案格式 */
buflen = strlen(buffer);
fstr = (char *)0;

for(i=0;extensions[i].ext!=0;i++) {
len = strlen(extensions[i].ext);
if(!strncmp(&buffer[buflen-len], extensions[i].ext, len)) {
fstr = extensions[i].filetype;
break;
}
}

/* 檔案格式不支援 */
if(fstr == 0) {
fstr = extensions[i-1].filetype;
}

/* 開啟檔案 */
if((file_fd=open(&buffer[5],O_RDONLY))==-1)
write(fd, "Failed to open file", 19);

/* 傳回瀏覽器成功碼 200 和內容的格式 */
sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr);
write(fd,buffer,strlen(buffer));


/* 讀取檔案內容輸出到客戶端瀏覽器 */
while ((ret=read(file_fd, buffer, BUFSIZE))>0) {
write(fd,buffer,ret);
}

exit(1);
}

main(int argc, char **argv)
{
int i, pid, listenfd, socketfd;
size_t length;
static struct sockaddr_in cli_addr;
static struct sockaddr_in serv_addr;

/* 使用 /tmp 當網站根目錄 */
if(chdir("/tmp") == -1){
printf("ERROR: Can't Change to directory %s\n",argv[2]);
exit(4);
}

/* 背景繼續執行 */
if(fork() != 0)
return 0;

/* 讓父行程不必等待子行程結束 */
signal(SIGCLD, SIG_IGN);

/* 開啟網路 Socket */
if ((listenfd=socket(AF_INET, SOCK_STREAM,0))<0)
exit(3);

/* 網路連線設定 */
serv_addr.sin_family = AF_INET;
/* 使用任何在本機的對外 IP */
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
/* 使用 80 Port */
serv_addr.sin_port = htons(80);

/* 開啟網路監聽器 */
if (bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr))<0)
exit(3);

/* 開始監聽網路 */
if (listen(listenfd,64)<0)
exit(3);

while(1) {
length = sizeof(cli_addr);
/* 等待客戶端連線 */
if ((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length))<0)
exit(3);

/* 分出子行程處理要求 */
if ((pid = fork()) < 0) {
exit(3);
} else {
if (pid == 0) { /* 子行程 */
close(listenfd);
handle_socket(socketfd);
} else { /* 父行程 */
close(socketfd);
}
}
}
}



後記

此 Web Server 只能在 Linux 或 Unix 上執行,不能在 MS 的環境下運作。

2007年9月23日 星期日

親手打造一個背景執行的Daemon程式

Standard
這是我在過去在 Linux 上撰寫微型 Web Server 時的筆記,目的是要讓我的程式能夠在背景執行,也就是要寫所謂的 Daemon 程式。關於 Daemon 的解釋,中文有人翻做『守護神程式』,若是對照在 DOS/Windows 系統底下的講法,就是所謂的『常駐程式』了。

Daemon 要怎麼寫呢?簡單來說:
使用系統呼叫 fork(),在背景複製一個自己繼續活下去,然後讓母體自我終結。


以下是一個簡單的範例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void main(void)
{
pid_t pid;
pid = fork();

if (pid>0) {
exit(0);
}

while(1) {
/* 子程式在系統背景跑無限迴圈 */
}
}

使用 fork() 後會回傳一個 pid 值,也就是行程處理 ID,這 ID 如果『小於零』,代表作業系統有問題令 fork() 失敗,無法複製生成一個新的程序。但是如果 fork 成功,會回傳零或是大於零的數字。

複製自己成功以後,需要了解的是,我們的程式有兩個正在執行,一個是母體,一個是剛被複製出來的子程式。或許你也注意到了,目前正在跑的程式是完全一樣的,要怎麼讓程式自己發現自己是本尊還是分身?關於這個問題,我們可以使用 pid 來判斷:如果得到的 pid 是『大於零』就是母體;得到『等於零』就是子程式。然後,只要讓母體自我終結就好,從範例中可以看到這段的程式判斷。

後記

這有點像電影『魔鬼複製人』的情節,在程式被複製成功以後,就告知本尊:『你該死去了。』對人來說,這是件很悲哀的事,尤其是對被複製的本尊來說。以後電腦開機前,想想你這一次的開機,會殺死多少生命,或許你就不願開機了﹝笑﹞。

2007年9月22日 星期六

讓SoundMAX Integrated Digital HD Audio在Linux唱出聲音

Standard
這幾天在一台 NB(IBM T61) 上安裝 Linux,除了這台 NB 的顯示卡在 X Server 上一定得用新版 nvidia 官方的驅動程式或是使用標準的 vesa driver 驅動外﹝註:在 Xorg 7.0 用 nv 不能動﹞,最大的問題就是聲音無法出來。我使用出廠預設的 Windows Vista 檢查硬體裝置後,發現 T61 使用的是『SoundMAX Integrated Digital HD Audio』,在陸續猜測各種聲音晶片的驅動程式下,終於成功的使用 snd-hda-intel 模組驅動聲音晶片。

以下是我驅動的過程:
modprobe snd-hda-intel
modprobe snd-pcm-oss
modprobe snd-mixer-oss

後記

由於我已經有好一陣子沒去下載使用各家 Linux 套件最新的版本使用,或許在它們新版的系統中已經解決了無法驅動 SoundMAX Integrated Digital HD Audio 的問題。

2007年9月18日 星期二

Kerrighed - 建置 Linux Cluster﹝叢集﹞的另一個選擇

Standard
說到叢集電腦(Cluster Computer),過去最容易建置的 Cluster 方案 openMosix,一向擁有許多的用戶和支持者,相較其他的專案如:OpenSSI,openMosix 對 Linux Kernel 的改動最小,而且,就它的發展歷史來看,也是取經於過去已經存在的 Mosix 架構,因此不論是在安裝建置或是可用性上,openMosix都是良好且值得一用的不二選擇。但是,openMosix在今年突然宣佈了將要停止開發的消息,這消息令許多人錯愕,也都紛紛尋找其他替代方案;就在這時候一向被認為不成熟的 Kerrighed 專案﹝http://www.kerrighed.org/﹞,在今年度開發的表現亮眼,逐漸跳脫出給人不成熟的印象。

Kerrighed 在不久前支援了 2.6.20 的新核心,也釋出了 2.0、2.1 版本,代表的是從 1.0 的概念實作,提升了到開始針對實用面的研發,並立即支援了多處理器和64位元架構的硬體。

以下是 Kerrighed 官方網站上的目前發展特色說明:


Current Features
  • Global Process Management
    • Cluster wide PIDs
    • Process migration with open files, pipes, sockets, shared memory segments, etc.
    • Mosix-like global process scheduler.
    • Full cluster wide UNIX process management interface (ps, top, kill, etc).
  • Global Memory Management
    • Support for distributed system V memory segments.
  • Checkpoint / restart
    • Checkpoint/restart of single processes (EXPERIMENTAL)

Kerrighed 在網站上的研發行程表示,將在年底改進執行程序轉移的效能,更會在明年增進數個關於記憶體、檔案暫存等特色,值得另人矚目。

後記

我將會在有空的時候詳細測試
Kerrighed 的能力,若有更多相關心得和問題,也將陸續記錄下來。

2007年9月15日 星期六

考作文必背的一段『論小人』

Standard
雖然還沒決定要搬去哪,但這個月是確定要搬家了。所以一有空就整理東西,不管是硬碟裡的資料,還是桌上書架上的資料。不過有趣的是,最近翻到了高中時所記錄的一段『論小人』文章段落。還記得,當時看到了一篇名叫『論君子與小人』的文章,作者我忘記了,其中講到小人這段當時實在是讓我覺得,如果寫作文時用出來一定非常的逗趣而且很酷,所以我當時就記錄下來並背了一小段。

小人者,心邪性乖,巧言令色,刻薄奸詐,投機取巧,慣用媚騙,愛占便宜,坐井觀天,妄自尊大,口是心非,狡滑無信,幸災樂禍,損人利己,驕奢淫佚,寡廉鮮恥,愛拍馬屁,喜獻殷勤,只知貪利,不知修身,只知利己,不知利人,記人之過,忘人之功,記人之仇,忘人之恩,不識大體,不顧大局,只顧眼前,不顧後果,患得患失,禍國禍民,尤其對人用心險惡,處處虛偽,對事強詞奪理,歪曲事實,對時愛弄玄妙,故意爽約,對地得寸進尺,任意侵占,對物無所不欲,任意貪求,性好動而不好靜,心望亂而不望治,且喜道他人之短,誇耀自己之長,其容貌不足以動人,其言語不足以眩世,雖具人類之面,確為禽獸之心,常蓄有違法亂紀之想,專門作傷天害理之事,寫匿名信,打小報告,成事不足,敗事有餘,有如此奸邪之心,醜惡之行者,可謂之真小人矣。

可惜的是,我們已經不是過去的八股年代,君子小人這種考試作文題目,早已經絕種。這段形容小人的文句,我看現在也只能截取精華片斷來罵人了﹝笑﹞。

2007年9月14日 星期五

跨入 Web 2.0 的時代!讓網站支援 RSS 即時書籤

Standard
曾經燒光了全世界投資人的錢,導致網路泡沫化的那群人,在他們臨死前的那一刻,喊出了 Web2.0 口號,他們企圖想用這新的名目來延續自己的燒錢事業。可是所謂的 Web 2.0 在技術上真的有重大突破嗎?答案是否定的,其實它與傳統 Web 只有觀念不同,所使用的技術並無多大改變,舊內容新包裝,完完全全是專門拿來詐騙股票投資人的幌子。但無知的媒體炒得沸沸揚揚,還以為 2.0 意指的是 Web 在技術上有所重大突破。

Web2.0 到底是什麼?事實上,Web2.0 指的只是網路發展的趨勢 - Service 導向,主要訴求是讓網路用戶選擇需要的專業服務,意即網路用戶將不再於網際網路上到處閒逛網站,而是會選擇專門的服務使用,例如:MSN即時通、 Blog、網路相簿、拍賣網、新聞網、Google搜尋。Service 導向的網站將提供專門的資訊給用戶,每個網站的功能很明確直接。另外,還有一種常見的應用,就是提供使用者做建立數位內容和交流的服務,以使用者提供的資訊當做網站主體。

雖然 Web2.0 這名詞只是炒股票用的,可是 Web2.0 推出後,確實改變了許多技術人員的思路,以 Web 2.0 所謂的 Service 導向設計了很多的應用技術。RSS即時書籤就是項目之一,它與傳統的『我的最愛』和『書籤』﹝bookmark﹞不同,除了提供原有網站書籤記錄功能外,還擁有自動更新資訊的能力,用戶不必再一一自行檢查各大網站是否有更新,任何的更新資訊將可以透過RSS傳遞到網路用戶的瀏覽器上,因此RSS即時書籤在即時新聞上有著很大的應用價值。因為使用了 XML 當資料標準格式,RSS獲得了可攜性高的優點,除了可以在電腦瀏覽器上,各種通訊裝置上也都可以使用。

一個簡單的例子讓網站支援 RSS 即時書籤

RSS即時書籤是建構在 HTTP 通訊協定之上,並採用 XML 當資料標準格式﹝傳統網頁是以 HTML 當資料標準格式﹞。

要使用 RSS 即時書籤必須先建立一個 XML 檔案:
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>我的RSS</title>
<item>
<title>新聞標題一</title>
<link>http://mywebsite/1.html</link>
</item>
<item>
<title>新聞標題二</title>
<link>http://mywebsite/2.html</link>
</item>
</channel>
</rss>


然後在網頁中 <HEAD> 和 </HEAD> 的中間加入:


<link rel="alternate" type="application/rss+xml" title="RSS feed" href="XML檔案的URL">



後記

XML的部份您也可以用 PHP、ASP或 CGI 等方式動態產生,然後和後台資料庫做結合以達到真正的即時效果。

2007年9月13日 星期四

PC 作業系統實作馬上入門

Standard
最近在整理過去的許多筆記,看到了很多懷念的東西,尋思當初許多的瓶頸,都缺少一個人幫我打通任督二脈,讓我一路走起來倍感艱辛,雖然不後悔這樣沒章法的強幹式學習,但許多冤枉路我心裡明白是白走了,尤其在作業系統這部份,對大多數人來說,更是不易突破的關卡。因為,看程式方面的書籍它不會教你,看硬體架構的書又不告訴你程式實作,如果想自己融會貫通,又因為沒有良好的入門範例,不知道該注意哪些事項而挫折連連。

開始前,你要有個體認,我們要寫的是作業系統,整台電腦都將由我們的程式完全控制,所以,如果你的程式不正常,將會落得停格當機下場,沒有『程式關閉,請聯絡...』或是『Core...』這套。另外,不要想說有任何 API 可以用,你都要一一自己打造這一切,因為系統開發本來就是一件辛苦的事,你正在開拓一個沙漠的綠洲。

而大多數人在學習作業系統撰寫時,常都會卡在第一步:我要如何讓自己的作業系統啟動?然後如同第一次學寫程式時,出現一行 Hello World?當初的我也在這問題上打轉好一陣子,縱使我會組合語言、有過不少寫程式經驗、看過許許多多的相關書籍,但我還是不知道該從哪開始。


我們先來了解電腦啟動的流程:
  1. 電腦開啟電源,會啟動 ROM 裡面的 BIOS 執行硬體檢查和初始化。
  2. BIOS 會依序搜尋 BIOS 設定中的啟動裝置(Boot Device),如硬碟。
  3. 一旦在某個啟動裝置上發現了啟動磁區(Boot Sector 大小為 512 Bytes),就會將此磁區 512 Bytes 的程式載入到記憶體位址 0000:7c00
  4. BIOS 會跳到記憶體位址 0000:7c00 執行被載入的程式,並將執行權全權交給它。


以下是一個 512 Bytes 的簡單開機程式實作範例﹝組合語言﹞:

org 07c00h ;此程式將被載入到 0000:7c00 執行
mov ax, cs
mov ds, ax
mov es, ax
call Disp ;呼叫 Disp 顯示字串
jmp $ ;無限回圈

Disp:
mov ax, BootMsg
mov bp, ax ;指定 BootMsg 的位址
mov cx, 16 ;BootMsg的字串長度
mov ax, 01310h ;ah=13, al=01h
mov dl, 0
int 10h ;中斷 10h 顯示字串
ret



BootMsg db "Hello, OS World!"
times 510-($-$) db 0 ;將編譯出來的程式填充為 510Bytes
dw 0xaa55 ;填入最後兩個結束字元 55 和 AA
此程式執行結果將會顯示『Hello, OS World!』,然後進入無限迴圈。

這程式編譯出來會是一個 512B 的小程式,你只要將它寫入硬碟或磁碟片的開始 512 磁區,然後用它們開機啟動就可以看到執行結果。如果你在 Linux 環境下,可用 dd 指令來寫入磁區。假若你嫌每次測試都要重新開機很浪費時間,可以另外準備一台電腦或是使用模擬器、虛擬機器程式,如 VMware等。

註:本文是針對 x86 系列的 PC 電腦架構所寫,所以無法在其他如 PPC、Alpha 等架構上實作。

2007年9月12日 星期三

喉嚨都要生豬豬﹝蜘蛛﹞網了

Standard
久違了 KTV 的我,在這個暑假期間精打細算下,光單單一個月就去 KTV 少說有三十幾次﹝汗﹞。大家都知道,一個月不是只有31天,怎麼會三十幾次?騙肖!我的解釋是,因為有時候是一天兩攤,所以才會三十幾次了!你可以說我是個瘋子,因為正常人是不會這樣天天唱的。

回想以前高中時,喜歡一群人去歡樂,一度迷上了 KTV 內狂歡的感覺﹝我不吸毒,我唱歌中毒。笑﹞,每次一有空、大考一考完、慶祝、慶生,就會和朋友去好樂迪報到,只有兩個人去唱也是常有的事,唱到最後都沒聲音了。那鬼叫胡鬧的年代,看似荒唐,但卻開啟了我與唱歌的不解之緣。

再來看看現在,其實之前我已經有一年半左右不曾去唱過歌,這段日子裡,我把去唱歌狂歡的癮給戒掉了,真的一度以為,我不會再是 KTV 的常客了,但最後事實不如我所預料的。就在兩個多月以前,也就是暑假開始前,一次大學朋友的慶生會中,再次勾起我過去唱歌的回憶、激情與瘋狂。讓我發現,其實這些是我一直無法忘卻的。於是,我在這暑假中,計劃一群過去的狂歡人馬,密集的到好樂迪報到﹝好樂迪暑假有特價方案﹞,去到幾乎都快要把每一家店的價格背起來了。這一去,從當紅的歌曲,到有些年紀的,再到老到不行的老歌﹝『月琴』我的最愛 XD﹞,都一一都點出來被我們摧殘。但話又說回來,許多戰友的喉嚨也都被摧殘得體無完膚而陣亡,讓我必須不停的去招齊人馬補上。

暑假結束的日子慢慢逼近,隨著好樂迪的價格也慢慢回調,我也開始厭倦了這段狂飆的日子,停止了這場無止盡與喉嚨的戰爭﹝不會壞的嗓子戰勝了心理的欲望﹞。但我還是太小覷了我心裡的唱歌大怪獸,在這幾天短暫的平靜日子,牠已經精力十足又準備爬出來大肆胡鬧一番了。

2007年9月11日 星期二

遠傳 3.5G 山頂洞人專用的大頻寬

Standard
遠傳 3.5G 是欺騙消費者的東西,我個人稱它為山頂洞人專用的大頻寬。因為,你要到越遠越偏僻的地方,最好是山頂上﹝前提要收得到訊號﹞才會有一流的速度!雖然遠傳號稱能 3.6M,但實際上半數時間比他的 2G 網路服務還要慢!不信嗎?騙你是小狗!

原因是因為 3.6M 只是你的網卡、手機與基地臺的傳輸速度能到這麼快,並不代表基地台對外的網路線路能提供這麼快的速度。經過在三的定點測試,其實從基地台對外的頻寬根本少得可以!只要同一地區用的人一多,速度別說比 2G 慢,56K 撥接都可以被稱為超大寬頻了!嚴重時甚至會有封包 Lost 的情況﹝在命令字元模式輸入 ping 168.95.1.1 會發現很多 Timeout 的情況﹞,簡單來說會導致根本無法上網!

尤其,這個月來遠傳主推 3.5G,使用者大量增加後速度的降低和不穩定更為明顯!半夜的平均下載速度從之前的200KB以上,掉到現在最高只有30KB左右(約240Kb),白天有15KB就要偷笑了,平均是7~9KB﹝在板橋江子翠測試的結果﹞,用中華電信的網頁測速,你還有機會看到難得一見的下行 3KB(24Kb)奇景。同一地區所有人分享那基地臺小小的頻寬,線路整個就塞爆﹝因為大家都讓它吃到飽﹞!

結論是辦 3.5G 根本就是白白繳錢給遠傳,根本分不到多少頻寬,你不會有吃到飽的感覺。除非,你是需要到處趴趴走上網、或住非鬧區的人﹝如果你是住在超級鬧區就不在此限,因為使用者多,遠傳自然配較多的網路頻寬在那﹞,不然,請不要退掉你的ADSL、更不要去辦遠傳的 3.5G。不旦會被綁約兩年,線路改善前的這幾個月你都會用得極痛苦!

後記

打電話去遠傳客服是沒有用的,他們會先告訴你在不同區域速度會不同,不然會說他們工程師會處理,弄到最後,不但問題沒解決,你還是要繼續繳費。用 3.5G 跑得比 2G 還慢,遠傳是把消費者當笨蛋嗎?

2007年9月10日 星期一

正在考慮轉移原本的 Blog 到這來

Standard
我並不是嫌棄原來在 blog.twpug.org 的 Blog﹝由此連入﹞ ,事實上,我反而非常喜歡它。一方面是它的網址和整個系統沒有商業的氣息,多了一分與世無爭的感覺﹝這也是為什麼我當初去申請的是它而不是無名、雅虎的原因﹞;另一方面,整個系統非常順暢快速﹝大概是只有我一個人在那發表文章吧XD﹞。只是,最近自從加入了 PostgreSQL 中文社群網誌的作者群之後,經常登入 Blogger ,也因此順便申請了它的 Blog。把玩的過程中,發現 Blogger 提供許多完整的功能模組,和完全可客制化的樣版架構,令人耳目一新。

但這陣子下來,發現在 Blogger 和 TWPUG Blog 兩邊跑實在是累人,一下弄完這邊,又要去管另外一邊。在疲憊之下,轉移原本的 Blog 到這來的想法就產生了,我必須要擇其一。一邊是複雜又商業化的環境,一邊是簡單平庸的世界,真是難做抉擇!

2007年9月9日 星期日

PL/pgSQL:呼叫函數、SQL查詢與回傳值處理

Standard

使用 PL/pgSQL 不外乎就是撰寫資料庫的預儲程序(Stored Procedure)、函數(Function)、觸發器(Trigger) 等。設計這些內部程序,其中除了迴圈、真假值判斷、回傳等等特別需求的語法外,主要的內容還是由各種 SQL 查詢命令或是呼叫其他已存在的函數所構成。由於使用 PL/pgSQL 呼叫其他函數或執行SQL命令時,多半是要等待回傳值、資料列並更進一步處理,所以與平時用前端程式或SQL命令列對函數與SQL指令的操作上,有比較不 同的習慣性用法。要了解在 PL/pgSQL 處理各種呼叫查詢,必須從回傳值處理的角度去深入。

以下是幾個常用處理函數和SQL命令回傳值的方法:

  1. 將回傳資料列指向 rs 變數﹝將 rs 定義為 record 類型﹞
    SELECT * INTO rs FROM mytable;

    然後可讀取回傳資料列的各欄位內容:

    rs.id
    rs.name
    rs.address
    ...

    註:此 SELECT INTO 使用方法很特別,並非是你想的那樣,請參考下文說明。


  2. 只回傳單獨欄位內容並指向 addr 變數﹝將 addr 定義成與 address 欄位類型相同﹞

    SELECT address INTO addr FROM mytable;


  3. 拋棄所有查詢和函數的回傳值
    EXECUTE myfunction();
    PERFORM myfunction();

    註:EXECUTE 和 PERFORM 的詳細差異不在本文討論範圍,請參考官方說明文件。


一般來說,在 PL/pgSQL 之中我們還是可以照常使用 SELECT、UPDATE、DELETE 等指令,差別在於執行查詢命令時『有無回傳值』。另外,以下有幾點,是使用 PL/pgSQL 呼叫及執行任何 SQL 命令時該注意的重點:


任何命令所回傳的值不能隨便忽略


在 過去使用前端外部程式去執行 SQL 命令,你可以忽略不管回傳值的問題,就算有回傳任何資料,我們也可以省略不處理它;但在 PL/pgSQL 中就有所不同了,尤其在『觸發器(Trigger) 』函數的設計中,更是不可放任回傳值不處理,所以,通常在觸發器中我們如果要拋棄回傳值,請使用 PERFORM 。


SELECT INTO 的不同


在 PL/pgSQL 與平時使用 SELECT INTO 是完全不同的意義,後者是建立一個新的資料表,並將所查尋到的數個資料列寫入此資料表。而在 PL/pgSQL 中只是取得查詢資料列到自訂變數之中,並不會建立新的資料表。

2007年9月5日 星期三

研發人員的心態

Standard
上個月初,Linux 的父親也就是 Linus ,對核心的未來做了一個重大的決定,也就是選擇了 CFS 這個在短短 62 小時內就被開發出來的行程排班器。這個決定讓大家熟知的 ck-patch 其創始者就此和 Linus 槓上了,因為 ck-patch 中包括著一個 SD 行程排班器,雖然經過多年的努力,卻一直遲遲無法被正式納入 Linux 核心;可是才短短的幾天之內就被完成的 CFS ,馬上就被 Linus 採用並準備納入下一版的核心之中。雖然 Linus 有充份且良好理由選擇 CFS 並放棄 SD,但這對於一個努力為自由軟體付出多年的程式研發人員,無疑是最大的打擊。

這個事件的確值得令人省思。由 Linus 放棄 SD 的理由可以知道,其實有很大的因素是程式人員的心態:

Linus 批 ck-patch 的創始者 Con kolivas 面對使用者回報的問題,總是爭論對抗,不肯用心與用戶一同解決,認為自己是正確完美的。


不只是在國外,國內企業裡 RD、研發工程人員和老闆之間也總是存在著相同的問題,老闆回報的問題和意見總是被研發人員駁斥爭論,一些無奈的老闆也只能暗自恨己不是專業人員,而有魄力的頭家在一氣之下乾脆就把整個部門給裁了。

暫且不論企業中的點點滴滴,相同問題在國內的自由軟體界也是存在著。面對著許多過路客的問題,也總是激烈爭論對抗,就因為他們不是使用自由軟體、他們對自由軟體存有疑慮或誤解。


我只能說,研發人員們,你們真的嚇到他們了!



另外一件有趣的事順帶一提,CFS 其實是我的中文姓名縮寫,其中更引伸了一段小小故事。

在多年前,我還是清純國中生﹝笑﹞的時候,就曾妄想寫一個如同 MD5 ﹝當時被資訊界視為非常安全的加密方法﹞一般的單向加密程式。正好,當時我正在經營一個 ASP 的相關技術網站 - ASP技術廣場﹝不知道還有誰記得?:p﹞,我理所當然的就直接使用 ASP 寫了一段當時覺得很棒的加密程式﹝現在回頭看,真的是覺得很丟臉!﹞,整個開發的時間就如同 CFS 排班器一樣,很短,大概只花了62分鐘﹝笑﹞,最後這個加密方法當然也叫做 CFS。

到這裡都還沒什麼,之後,我開心的將這 CFS 加密函式放在網站上供所有人下載使用,並寫了一篇教學文章教大家如何用它,這篇文章標題是:『加密處理使密碼更安全[CFS編碼加密] 』,常逛大陸網站的人或許有些印象,沒錯!你現在從 Google 去搜尋還找得到它被無限轉載的蹤影,甚至連簡體版、加註版都有了!某次意外的閒逛,還發現 CFS 編碼加密被重寫移植成 PHP 、 .Net 版!?在簡體的大陸網站上,除了有不少人在討論這個加密法,更有商業產品標榜使用 CFS 來當他的產品特色,尤其在前陣子MD5被破解後,更是無一不推崇自己的產品使用了 CFS。

但是,他們好像不知道為什麼這加密方法叫 CFS ,更不曉得其實他的生父正在無言的冒冷汗,因為,CFS 怎麼能跟 MD5 比?尤其在 MD5 被破解的今天,CFS 又有什麼面目見江東父老?有誰能幫我跟他們說說呢?

PS. 這 CFS 指的不是 Linux 上的 Crypt File System