2009年8月26日 星期三

如何防止彊屍程序(zombie)的產生??

當執行program ex1程式片段後, 試著以kill prog1的方式終止prog1之程序,
結果查詢目前各程序執行狀況(ps -ef), 發現出現[prog1],
接下來無論怎麼kill prog1, 都無法將其消滅, 唯有中止main(), 它才會消失...

原來它就是僵屍程序(zombie), 何謂僵屍程序呢??
在fork()/execve()過程中,假設子程序結束時父程序仍存在,
而父程序fork()之前既沒設置SIGCHLD信號處理函數調用waitpid()等待子進程結束,
又沒有設置忽略該信號,則子程序成為僵屍程序,無法正常結束,
即使是root身份kill -9也不能殺死僵屍程序。

補救辦法是殺死僵屍程序的父程序(僵屍程序的父程序必然存在),
僵屍程序成為"孤兒程序",過繼給pid=1的程序init,init始終會自行負責清理僵屍進程。


僵屍程序之範例:

int main (int argc, char *argv[])
{
char *prog_list = {"prog1","prog2", "prog3"};

for (int i=0; i<3; i++) {
char *arg_list = {prog_list[i], NULL};
forkExecProc (prog_list[i], arg_list);
}

while (true) {
sleep (2);
}

return 0;
}

int forkExecProc (char *prog, char **arg_list)
{
pid_t child;

/* check if fork fail that first child is not created */
if ((child = fork ())< 0) {
fprintf (stderr, "fork error");
} else if (child == 0) { /* run into first child */
fprintf (stdout, "fork to execute : [%s]\n", arg_list);
/* replaces the current process image with a new process image */
execvp(prog, arg_list);

/* if execvp() is return, mean that on error */
fprintf(stderr, "execvp error");
exit(0);
}

return child;
}

解決一.
在父程序設置SIGCHLD信號的處理函式, 使其父程序自動忽略子程序的狀態變更,
但長期常駐程式, 可能不適用, 難保不會出現系統資源耗用的現象

int main (int argc, char *argv[])
{
signal (SIGCHLD,SIG_IGN); //設置SIGCHLD
char *prog_list = {"prog1","prog2", "prog3"};

for (int i=0; i<3; i++) {
char *arg_list = {prog_list[i], NULL};
forkExecProc (prog_list[i], arg_list);
}

while (true) {
sleep (2);
}
return 0;
}
int forkExecProc (char *prog, char **arg_list)
{
...
}

解決二.
在父程序設置SIGCHLD信號的處理函式, 並呼叫waitpid(), 等待捕獲子程序的返回狀態


void sig_fork(int signo)
{
pid_t pid;
int stat;
// 呼叫waitpid(),等待子程序返回, 若無子程序返回, 也不一直等待
pid=waitpid(0,&stat,WNOHANG);

return;
}


int main (int argc, char *argv[])
{
signal (SIGCHLD, sig_fork); // 設置SIGCHLD, 並呼叫waitpid(), 捕獲子程序的返回狀態

char *prog_list = {"prog1","prog2", "prog3"};

...

return 0;
}


int forkExecProc (char *prog, char **arg_list)
{
pid_t child;

/* check if fork fail that first child is not created */
if ((child = fork ())< 0) {
fprintf (stderr, "fork error");
} else if (child == 0) { /* run into first child */
fprintf (stdout, "fork to execute : [%s]\n", arg_list);
/* replaces the current process image with a new process image */
execvp(prog, arg_list);

/* if execvp() is return, mean that on error */
fprintf(stderr, "execvp error");
exit(0);
}

/* no block to wait for first child chang state */
/* must be use signal (SIGCHLD, xxx) to fetch child change state */
waitpid (-1, NULL, WNOHANG); // 父程序呼叫waitpid(),不阻塞等待子程序的返回狀態, 待引發SIGCHLD

return child;
}

解決三.
呼叫2次fork(), 父程序呼叫fork(第一次)產生子程序, 子程序再呼叫fork(第二次)產生孫程序,
隨即子程序終結死亡, 此時孫程序變為"孤兒程序",init程序會接管孫程序, 變成它的父程序,
而init程序會自行負責處理SIGCHLD信號


int main (int argc, char *argv[])
{
char *prog_list = {"prog1","prog2", "prog3"};

for (int i=0; i<3; i++) {
char *arg_list = {prog_list[i], NULL};
forkExecProc (prog_list[i], arg_list);
}

while (true) {
sleep (2);
}

return 0;
}

int TaskHandler::forkExecProc (char *prog, char **arg_list)
{
pid_t child;

/* check if fork fail that first child is not created */
if ((child = fork ())< 0) { // 產生子程序
fprintf (stderr, "fork error");
} else if (child == 0) { /* run into first child */

/* check if fork fail that second child is not created */
if ((child = fork ())< 0) { // 產生孫程序
fprintf (stderr, "fork error");
}
else if (child > 0) { /* run into parent of second child whick is first child */
/* terminate the first child, in order that second child's parent becomes init */
exit(0); // 子程序自行終結, 此時孫程序被init接管為它的父程序
}
else { /* run into second child */
// 孫程序繼續執行下列步驟
fprintf (stdout, "fork to execute : [%s]\n", arg_list);
/* replaces the current process image with a new process image */
execvp(prog, arg_list);

/* if execvp() is return, mean that on error */
fprintf(stderr, "execvp error");
exit(0);
}
}

/* wait for first child chang status */
waitpid (child, NULL, 0); // 父程序呼叫waitpid(), 等待子程序終結,並捕獲返回狀態

return child;
}

2009年7月5日 星期日

dblink of postgresql

現今在承接南部第一大鋼鐵廠的資訊系統工程後, 遭遇到必須連線至遠端的postgresql,
擷取所需的資料, 並不透過Tcp/Ip Socket進行訊息封包的傳送

便回想當初在第一家電子公司當小小MIS, 便曾使用過Oracle其一功能, 簡稱db link,
不必在ap上, 另外多建立一個資料庫連線, 可利用原資料庫連線擷取另遠端資料庫的功能

於是上網google, 果不其然postgresql也支援著類似的功能, 不愧是自由軟體界資料庫第一把交椅,
而安裝動作也很簡單, 整理如下所述:

## 安裝 (至存放原始安裝檔之路徑)
$> cd contrib/dblink
$> make
$> make install

## dblink相關函式安裝 (至postgresql安裝目錄之路徑)
## 此時會新增兩個檔案 pgsql/lib/dblink.so(函式庫) & pgsql/share/contrib/dblink.sql(語法安裝)
$> su - postgres
$> cat dblink.sql psql

參考網址: db link sql

2009年5月23日 星期六

Difference between Big Endian and Little Endian

Difference between Big Endian and Little Endian

原文釋意:
"Little Endian" means that the low-order byte of the number is stored in memory at the lowest address, and the high-order byte at the highest address. (The little end comes first address.)

"Big Endian" means that the high-order byte of the number is stored in memory at the lowest address, and the low-order byte at the highest address. (The big end comes first address.)

中文釋意:
為什麼叫『大頭』派?因為電腦把『大』的位數放在『前面』 (記憶體編號小的就是前面)。就好像我們寫十進制數字 123 的意思一百二十三 (100 + 20 + 3),也就是最大位--百位,寫在前面。
相反地,如果把『小』的位數放在『前面』,那就是『小頭』派了。如果有一個民族的文字,把 123 解釋成三百二十一 (1 + 20 + 300),那他們就是『小頭派』。

阿拉伯文的文字書寫,是從右向左橫寫,但是遇到數字的時候,卻是跟我們一樣從左向右寫。如果阿拉伯人讀文字與數字的時候,都是從右向左讀,則他們會先讀到數字的最小位。在這個意義之下,阿拉伯人是『小頭派』。

int i = 2562 + 2x256 + 3 = 00000000 00000001 00000010 00000011 位元排列方式 = 0 1 2 3 無號整數方式

CPU 一定會配給連續四個記憶體給 i,但是這四個記憶體,卻有兩種放置四個 byte 的可能順序。如果我們一律按照記憶體位址從小到大的順序來講,則四個記憶體放置的字元可能是先 00000000 然後 00000001 然後 00000010 最後 00000011

Lo-------------->Hi (Big Endian)
+-+-+-+-+-+-+-+-+-
00 01 02 03
+-+-+-+-+-+-+-+-+-
Hi<--------------Lo (Little Endian)

2009年4月22日 星期三

How to read .ini file by using glib

在Windows API中, 提供相當方便的函式庫, 可以簡單地讀取.ini檔案,
但在c++ standard library中, 卻不見著任何蹤跡,
雖說自己很喜歡coding, "蛋"不是這麼搞的啦!!

google search提供了一個相當不錯的工具 -- glib

不免俗地要介紹一下本文主角- glib 。

gnome是基於gtk+開發的一套桌面環境,gnome和KDE作為兩大最流行的桌面環境,在全世界廣泛使用。 只要是在Linux下工作的開發人員,對於gtk+一定不陌生。 而對於glib,這個gtk+下的無名英雄,其功能強大卻鮮為人知。

glib不是glibc,儘管兩者都是基於GPL的開源軟體。但這一字之差卻誤之千里,glibc是GNU實現的一套標準C的庫函數,而glib是gtk+的一套函數庫。
在linux平臺上,像其他任何軟體一樣,glib依賴於glibc。glib不是一個學院派的東西,也不是憑空想出來的,完全是在開發gtk+的過程中,慢慢總結和完善的結果。

如果你是一個工作3年以上的C語言程式師,現在讓你講講寫程式的苦惱,你可能有很多話要說,但如果你有時間研究一下glib,你會發現,很多苦惱已不再成其為苦惱,glib裏很多東西正是你期望已經久的。

gobject是glib的精粹,glib是用C實現的,但在很大程式是基於面向物件思想設計的,gobject是所有類的基類。signal在其中也是一大特色,signal與作業系統中的signal並不一樣,它是類似消息一樣的東西,讓消息在各個物件間傳遞,但儘量降低物件間的耦合。

1. setup the glib

$> ./configure
$> make
$> make install
2. include glib header files and library to compile
##顯示glib include的路徑
$> pkg-config --cflags glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include

##顯示glib library的路徑
$> pkg-config --libs glib-2.0
-L/usr/lib -lm -lglib-.20

並在程式的標頭檔加入下列文字
#include "glib.h"

完成上述兩個簡單的步驟, 就可以親身去體會glib的強大之處囉!!

參考來源::
GLib Reference Manual
Wiki GLib

2009年4月14日 星期二

指標的指標 (pointer to pointer)

int **iptr;
上面是宣告了一個指標指向另一int的指標, 很饒舌對吧!簡單說, 就是指標的指標 (pointer to pointer)

何以需要這樣的用法?在網路上拜讀了某位前輩的文章的範例, 突然間茅塞頓開...



int gint = 0;
void changePtr (int *pInt)
{
pInt = &gint;
}
void main ()
{
int local_int = 1;
int *localPtr = &local_int;
changePtr (localPtr);
printf ("%d\n", *localPtr);
}



上述例子, 印出來的數值仍為1, 因為changePtr的pInt是localPtr的複本, 對pInt做變更, 其實並不會影響到localPtr本身

使用call by pointer (or address)來傳遞參數, 被呼叫的函式只是複製pointer的值過去罷了!
所以當我們想在函式內改變外來的pointer的值(非pointer所指向的變數), 且函式外部能使用其改變的pointer的值, 此刻就是call by pointer to pointer登場的最佳機會!

下述例子, 印出來的數值變為gint的0, changePtr()的pInt是&localPtr的複本, 對其做任何變更, 並不會去影響到localPtr本身, 但在changePtr(), 我們是對pInt所指向的內容(就是localPtr)做變更, 由原本存放local_int的address, 變更為存放gind的address




int gint = 0;
void changePtr (int **pInt)
{
*pInt = &gint;
}
void main ()
{
int local_int = 1;
int *localPtr = &local_int;
changePtr (&localPtr);
printf ("%d\n", *localPtr);
}

2009年3月22日 星期日

如何在vim支援doxygen彩色語法

若在vim中, 可以支援doxygen的 彩色語法, 一定可以大大增加可讀性
沒想到, 這是真的, 這下子在註解時, 更可以把doxygen發揮到極限

其實步驟非常簡單:
  1. 於home directory下, 建立.vim/after/syntax的目錄
  2. 下載doxygen.zip (v1.15)
  3. 解壓縮上述檔案, 將doxygen.vim & doxygen.txt複製至~/.vim/after/syntax
  4. 針對每一個想到支援doxygen syntax coloring的檔案類型, 建立soft link
$> cd ~/.vim/after/syntax
ln -s doxygen.vim c.vim
ln -s doxygen.vim cpp.vim
ln -s doxygen.vim java.vim

參考來源:
Doxygen syntax coloring in vim

2009年3月14日 星期六

What is Twitter??

Twitter是在中山大學學分班課程中, 黃慶祥教授提及的名詞,
只是想不到全班30來人的資訊人, 知道這個名詞的人竟是小熊兩三隻,
想當然爾我是隻小狗, 而不是小熊!!

查了一下譯典通, Twitter = 吱吱喳喳 = 三姑六婆 = 閒話家常

google search 該字詞, 文字解釋不少, 但我喜歡以下這段影片以生動的情境描述,
Twitter可以在你我的日常生活中, 扮演著什麼樣的功能角色??







影片中, 一開場就說了句很關鍵的話, "What are you doing?" (你在做什麼呢??)
在日常生活中, 只是一句很直覺的關心語句, 但在網路上卻無法很直覺地回答朋友們,
又或者是, 想輕鬆簡單地分享一則資訊, 例如"今日的天空很湛藍",
也許有人會說, 透過e-mail、blog、msn...等等, 但這會不會太小題大作了呢??
我收e-mail最不喜愛看到一些無關要緊的信件,
而blog也不希望記錄下這毫無參考價值的訊息,
在msn要告訴大家, 就得一個一個開啟視窗, 一個一個地訊息傳送..
哇..這麼簡單的小事, 卻要歷經這麼繁瑣的過程, 這會不會本末倒置了!!

Twitter就是可以讓你輕易分享你的生活小事給周遭的朋友知道,
在一個頁面上, 就可以分享交流彼此朋友的生活點滴, 而且每則消息不超過140個字,
例如"我在高雄明誠星巴克享受午后咖啡時光", 透過Twitter分享,
你的朋友隨即可以知悉你最新的動態、心情、作息,
或許他也正巧在左營附近, 就可以順道前往與你共享一杯咖啡的悠閑時光

參考資料

2009年3月13日 星期五

How to format date and time by using C lib

在Linux中, 如何有效快速地格式化時間為字串格式呢??
原來有個系統函式可以簡易地達到這樣的目的,

size_t strftime(char *s, size_t max, const char *format,const struct tm *tm);

s : 存放日期時間轉換為字串格式的buffer
max : 上述s的最大長度
format : 格式化符號
tm : 日期時間結構

example:

int main () {
char msg[256];
time_t now;
struct tm *timestr;

time (&now);
timestr = localtime (&now);
strftime (msg, sizeof(msg),"%Y%m%d%H%M%S", timestr);
printf ("the datetime is %s \n", msg);
}

則會輸出以下字串
the datetime is 20090315093045


出乎意料地簡單吧!!

2009年3月1日 星期日

TCP TIME_WAIT的釋義

在實務中, 許多情況下, 可能是Server Socket出現了問題, 勢必採取關閉原有的Sever Socket, 再隨即重新啟動新建的Server Socket. 但此刻會發生一個問題, 就是相隔時間過短, 在新建的Sever Socket進行bind()的程序時, 系統會出現一錯誤訊息"the address already in use".

照一般想法, Server Socket都已Close, 理應已釋放出該IP Address之資源, 怎會出現這個令人莫名的訊息呢??

關鍵在於TCP建立於多次的握手協定的基礎上, 以達到保證訊息傳遞的完整性.
由於TPC是全雙工傳輸, 換言之, 雙向的傳輸必須單獨進行關閉, 原則上主動請求關閉的一方A, 藉由發送FIN來請求終止這個方向的連接. 被動關閉的一方B, 收到FIN表示A-->B的方向已無資料進行傳送, 此時B-->A仍是可以進行資料傳送, 待B執行被動關閉.
簡言之, A欲進行關閉傳輸時, 必須通知B並確認, 而B欲進行關閉傳輸時, 也必須通知A並確認.



關閉連線:(如上圖所示)

(1) 當處於ESTABLISHED狀態時, TCP B欲主動關閉連線, 發送FIN至TCP A, 進入到FIN-WAIT-1狀態, 等待TCP A回應ACK, 表示等待確認TCP A得知TCP B要關閉連線

(2) 當TCP A收到TCP B的FIN時, 立即回應ACK給TCP B, 進入到CLOSE-WAIT狀態, 表示須等到應用程序沒有任何資料要傳送給TCP B, TCP A才決定關閉連線

(3) TCP B收到TCP A回應的ACK, 表示確認TCP A得知TCP B要關閉連線, 此刻等待TCP A發送FIN

(4) TCP A決定關閉連線, 發送FIN至TCP B, 進入到LAST-ACK狀態, 等待TCP B回應ACK, 表示等待確認TCP B得知TCP A要關閉連線

(5) TCP B收到TCP A的FIN後, 隨即回應ACK, 進入TIME_WAIT狀態, 表示TCP B得知TCP A要關閉連線, 且等待2MSL時間, 以防TCP A再次發送FIN

(6) TCP A收到TCP B回應的ACK後, 進入CLOSED狀態, 表示TCP A已確認TCP B已得知它要關閉連線, 才進行關閉連線

(7) TCP B等待2MSL時間, 才進入CLOSED狀態, 關閉連線, 並自連線表中移除

在上述的第(5),(7)點, 此刻TIME-WAIT的用意在於, 雖TCP B已確認TCP A要關閉連線, 且回應了ACK給TCP A, 但不保證TCP A會收到ACK, 一旦ACK遺漏, TCP A會再次發送FIN給TCP B, 再次進行確認, 所以TCP B須進入TIME-WAIT狀態, 等待2MSL時間, 預防TCP A會再次發送FIN, 進行連線關閉的確認


different states of a TCP connection:
LISTEN : awaiting a connection request from client (監聽客戶端的連線請求)
SYN-SENT : a SYN has been sent to server, and client is awaiting the ACK of SYN (發送SYN, 並等待服務端回應SYN的ACK)
SYN-RECEIVED : a SYN has been received from client, a SYN with ACK has been sent to client, and server is awaiting the ACK of SYN (接收客戶端的SYN, 另傳送SYN+ACK(SYN)給客戶端, 並等待客戶端回應SYN的ACK)
ESTABLISHED : the three-way handshake has been completed, and established the connection (完成握手協定, 並建立連線)
FIN-WAIT-1 : the local AP has issued a close. active TCP has sent a FIN to passive TCP, and is awaiting an ACK of FIN (主動端發出FIN至被動端, 並等待被動端回應FIN的ACK)
FIN-WAIT-2 : a previous FIN has been sent to passiveTCP, and received an ACK of FIN from passive TCP. active TCP is awaiting a FIN from the passive TCP (成功接收到先前傳送至被動端FIN的ACK, 此刻等待被動端傳送FIN)
CLOSE-WAIT : passive TCP has received a FIN from active TCP, and has sent an ACK of FIN to active TCP. passive TCP is awaiting a close request from remote AP before sending a FIN (被動端已接收主動端的FIN, 並傳送FIN的ACK至主動端, 此刻等待AP要求關閉連線)
LAST-ACK : previously a FIN has been received from active TCP, and an ACK of FIN has been sent to active TCP, and a FIN has been sent to active TCP. passive TCP is awaiting an ACK of FIN (先前收到主動端的FIN, 且傳送FIN的ACK給主動端, 另被動端傳送FIN給主動端, 此刻被動端等待主動端回應FIN的ACK)
TIME-WAIT : FINs have been received and ACK has been sent passive TCP. active TCP is waiting 2MSLs to remove the connection from the connection table (收到被動端的FIN, 並傳送ACK至被動端, 此刻主動端會等待2MSL的時間才將連線關閉)
CLOSED : a connection has been removed from the connection table (連線不存在於連線表)

參考資料:
Transmission Control Protocol
TCP State Diagram
TCP Connection Termination
Socket FAQ -- 2.7 please explain the TIME_WAIT state


2009年1月8日 星期四

使用SVN進行版本控制 - 相關參考資源

#####官方網站#####

TortoiseSVN -- 常見的SVN Client 軟體
http://tortoisesvn.net/

Subversion線上電子書 -- 繁體中文
http://twpug.net/docs/Subversion/

Subversion線上電子書 -- 簡體中文
http://svnbook.red-bean.com/

#####教學討論#####

簡體中文SVN的技術論壇, 資源非常豐富
http://www.svn8.com/

國內Blog, 有著圖文並茂的介紹及教學, 值得前往
http://i.repeat.tw/blog/category/107765

國內邱茂森前輩的簡報
http://www.csie.ntut.edu.tw/sdrc/files/course/20061201/VersionControlWithSVN.pdf

#####常見問題#####

Total Commander也可以看到SVN的系統圖示唷!!
http://www.dotblogs.com.tw/chhuang/archive/2008/10/12/5662.aspx

在選定一目錄Show log時, 某些版本只顯示版本號碼與 no date, 看不見其訊息, 為什麼??
http://doc.iusesvn.com/show-33-1.html

SVN是否可以控制中文目錄的訪問權限
http://doc.iusesvn.com/show-31-1.html

Client端連線至檔案庫時, 出現右列錯誤訊息 " svnserve.conf : 12 : Option expected "
http://doc.iusesvn.com/show-28-1.html

在Redhat AS4 的安裝與設定
http://doc.iusesvn.com/show-15-1.html

svnserve的權限設定
http://doc.iusesvn.com/show-8-1.html

Redhat AS4 安裝 apache2.2.3 + svn1.4 + 郵件自動通知
http://doc.iusesvn.com/show-7-1.html

詳盡介紹多層次目錄權限控制設定
http://doc.iusesvn.com/show-1-1.html

2009年1月7日 星期三

使用SVN進行版本控制 - (五)TortoiseSVN 的基本操作與使用

TortoiseSVN是在Windows平台上使用的Subversion Client, 它是免費的, 重要的是它把Subversion Client具備的功能全部整合於右鍵的功能選單內, 使用起來相當便利, 省去了下達Command的麻煩。
















以系統圖示表達目前檔案的狀態

提供不同的圖示, 各代表著不同的意義, 非常直覺化, 一目瞭然!!












瀏覽檔案庫的內容

任意地按下滑鼠右鍵, 點選『TortoiseSVN』-> 『Repo-browser』, 鍵入"svn://svnserver/repository", 瀏覽SVN Server的檔案庫內容





導出檔案庫副本
為了取得檔案庫的副本, 必須進行Check Out的操作。
選擇欲存放檔案庫副本的資料夾, 按下滑鼠右鍵, 點選『TortoiseSVN』-> 『SVN Checkout..』, 指定欲導出(check out)檔案庫的來源路徑, 再指定存放檔案庫副本的目的路徑。在此, Head Revision表示導出最新版本, 隨即導出檔案庫副本。





提交異動至檔案庫
在提交異動之前, 必須確認目前檔案庫副本是否仍為最新版本(表示這期間無人進行修改), 選擇存放檔案庫副本的資料夾, 按下滑鼠右鍵, 點選『TortoiseSVN』-> 『SVN Update』。



若本機異動的檔案, 在這段期間沒有他人進行修改, 則點選 『TortoiseSVN』-> 『SVN Commit』, 鍵入詳細的異動資料, 異動檔案, 異動人員...等等, 以利往後回溯至需要的版本



若本機異動的檔案, 在這段期間已有他人進行修改, 並且先行提交異動至SVN Server, 此時會蹦現出警告視窗, 提醒該檔案已提交較新的版本, 也顯示其較新版本的Log。遭遇此一情況時, 必須使用
『TortoiseSVN』-> 『Diff』, 進行檔案內容的差異化比較, 待與他人協調並確認檔案內容之後, 再提交經確認異動的檔案至SVN Server