[技術] 主機板的叫聲

Written on 10:56 上午 by Yu Lai

1. AMI BIOS
 1短——記憶體重新整理失敗
 2短——記憶體ECC校驗錯誤
 3短——系統基本記憶體(第1個64K)檢查失敗
 4短——系統時鐘出錯
 5短——中央處理器(CPU)錯誤
 6短——鍵盤控制器錯誤
 7短——系統真實模式錯誤,不能切換到保護模式
 8短——顯示記憶體錯誤(顯示記憶體可能有所損壞)
 9短——ROM BIOS檢驗和錯誤
 1長3短——記憶體錯誤(記憶體損壞,請更換)
 1長8短——顯示測試錯誤(顯示器資料線鬆動或顯示卡沒插穩)
2. Award BIOS
 1短——系統正常啟動
 2短——一般錯誤,請進入CMOS SETUP重新設置不正確的選項
 1長1短——RAM或主機板出錯
 1長2短——顯示錯誤(顯示器或顯示卡)
 1長3短——鍵盤控制器錯誤
 1長9短——主機板FlashRAM或EPROM錯誤(BIOS損壞)
 不斷地響(長聲)——記憶體沒插穩或損壞
 不停地響——電源、顯示器和顯示卡沒有連接好
 重複短響——電源故障
 無聲音無顯示——電源故障

3. Phoenix BIOS
 1短——系統正常啟動
 3短——系統加電自檢啟始化(POST)失敗
 1短1短2短——主機板錯誤(主機板損壞,請更換)
 1短1短3短——主機板電池沒電或CMOS損壞
 1短1短4短——ROM BIOS校驗出錯
 1短2短1短——系統實時時鐘有問題
 1短2短2短——DMA通道啟始化失敗
 1短2短3短——DMA通道頁寄存器出錯
 1短3短1短——記憶體通道重新整理錯誤(問題範圍為所有的記憶體)
 1短3短2短——基本記憶體出錯(記憶體損壞或RAS設置錯誤)
 1短3短3短——基本記憶體出錯(很可能是DIMM槽上的記憶體損壞)
 1短4短1短——基本記憶體某一地址出錯
 1短4短2短——系統基本記憶體(第1個64K)有奇偶校驗錯誤
 1短4短3短——EISA總線時序器錯誤
 1短4短4短——EISA NMI口錯誤
 2短1短1短——系統基本記憶體(第1個64K)檢查失敗
 3短1短1短——第1個DMA控制器或寄存器出錯
 3短1短2短——第2個DMA控制器或寄存器出錯
 3短1短3短——主中斷處理寄存器錯誤
 3短1短4短——副中斷處理寄存器錯誤
 3短2短4短——鍵盤時鐘有問題,在CMOS中重新設置成Not installed來跳過POST
 3短3短4短——顯示卡RAM出錯或無RAM,不屬於致命錯誤
 3短4短2短——顯示器資料線鬆了或顯示卡沒插穩或顯示卡損壞
 3短4短3短——未發現顯示卡的ROM BIOS
 4短2短1短——系統實時時鐘錯誤
 4短2短3短——鍵盤控制器(8042)中的Gate A20開關有錯,BIOS不能切換到保護模式
 4短2短4短——保護模式中斷錯誤
 4短3短1短——記憶體錯誤(記憶體損壞或RAS設置錯誤)
 4短3短3短——系統第二時鐘錯誤
 4短3短4短——實時時鐘錯誤
 4短4短1短——串行口(COM口、滑鼠口)故障
 4短4短2短——並行口(LPT口、列印口)錯誤
 4短4短3短——數學協處理器(8087、80287、80387、80487)出錯

[閒聊] 適合coding的字型(font)

Written on 10:28 下午 by Yu Lai

http://www.proggyfonts.com/

其實也算分享啦,用起來版面給人很整齊舒適的感覺,
字母和符號有特別設計過,置中、固定寬度,但是只有one size。
作者說專門設計給code listing,偶爾換換字型也不錯^__^。

[閒聊] 淺談兔子是怎樣吃掉狼的

Written on 12:25 上午 by Yu Lai

一隻兔子在山洞前寫文章,一隻狼走了過來,問:

“兔子啊,你在幹什麼?”

兔子答曰:“寫文章。”

狼問:“ 什麼題目?”

兔子答曰:“《淺談兔子是怎樣吃掉狼的》。”

狼哈哈大笑,表示不信,於是兔子把狼領進山洞。

過了一會,兔子獨自走出山洞,繼續寫文章。

一隻野豬走了過來,問:“兔子你在寫什麼?”

兔子答:“文章。”

野豬問:“題目是什麼?”

兔子答:“《淺談兔子是如何把野豬吃掉的》。”

野豬不信,於是同樣的事情發生。

最後,在山洞裡,一隻獅子在一堆白骨之間,

滿意的剔著牙讀著兔子交給它的文章:

題目是:“《一隻動物,能力大小關鍵要看你的老闆是誰》。



這隻兔子有次不小心告訴了他的一個兔子朋友,

這消息逐漸在森林中傳播;

獅子知道後非常生氣,他告訴兔子:

“如果這個星期沒有食物進洞,我就吃你。”

於是兔子繼續在洞口寫文章

一隻小鹿走過來,“兔子,你在幹什麼啊?”

兔子答:“寫文章”

小鹿問:“什麼題目?”

兔子答曰:“《淺談兔子是怎樣吃掉狼的》。”

“哈哈,這個事情全森林都知道啊,你別胡弄我了,

我是不會進洞的”

“我馬上要退休了,獅子說要找個人頂替我,

難道你不想這篇文章的兔子變成小鹿麼?”

小鹿想了想,終於忍不住誘惑,跟隨兔子走進洞裡。

過了一會,兔子獨自走出山洞,繼續寫文章

一隻小馬走過來,同樣是事情發生了。

最後,在山洞裡,一隻獅子在一堆白骨之間,

滿意的剔著牙讀著兔子交給它的文章:

題目是:《如何發展下線動物為老闆提供食物》



隨著時間的推移,獅子越長越大,

兔子的食物已遠遠不能填飽肚子。

一日,他告訴兔子:“我的食物量要加倍,例如:

原來4天一隻小鹿,現在要2天一隻,

如果一周之內改變不了局面,我就吃你。

於是,兔子離開洞口,跑進森林深處,他見到一隻狼

“你相信兔子能輕鬆吃掉狼嗎”

狼哈哈大笑,表示不信,於是兔子把狼領進山洞。

過了一會,兔子獨自走出山洞,繼續進入森林深處

這回他碰到一隻野豬----“你相信兔子能輕鬆吃掉野豬嗎”

野豬不信,於是同樣的事情發生了。

原來森林深處的動物並不知道兔子和獅子的故事

最後,在山洞裡,一隻獅子在一堆白骨之間,

滿意的剔著牙讀著兔子交給它的文章

題目是:《如何實現由坐商到行商的轉型為老闆提供更多的食物》



時間飛快,轉眼之間,兔子在森林裡的名氣越來越大

因為大家都知道它有一個很厲害的老闆

這只小兔開始橫行霸道,欺上欺下,沒有動物敢惹牠

兔子時時想起和烏龜賽跑的羞辱

牠找到烏龜說:“ 三天之內,見我老闆!”揚長而去

烏龜難過的哭了

這時卻碰到了一位獵人,烏龜把這事告訴了他

獵人哈哈大笑

於是森林裡發生了一件重大事情

獵人披著獅子皮和烏龜一起在吃兔子火鍋

地下丟了半張紙片歪歪扭扭的寫著:

《山外青山樓外樓,強中還有強中手啊》!!



在很長一段時間裡森林裡恢復了往日的寧靜,

兔子吃狼的故事似乎快要被大家忘記了

不過一隻年輕的老虎在聽說了這個故事後,

被激發了靈感於是他抓住了一隻羚羊,對羚羊說,

如果你可以像以前的兔子那樣為我帶來食物那我就不吃你。

於是,羚羊無奈的答應了老虎,而老虎也悠然自得的進了山洞。

可是三天過去了,也沒有見羚羊領一隻動物進洞。

他實在憋不住了,想出來看看情況。

羚羊早已不在了,他異常憤怒。

正在他暴跳如雷的時候突然發現了羚羊寫的一篇文章

題目是:《想要做好老闆先要懂得怎樣留住員工》

[技術] Linux具名管線(FIFOs)編程

Written on 8:39 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/16/1531201.aspx

具名管線可以用於任何兩個程序間通信,因為有名字可引用。注意管道都是單向的,因此雙方通信需要兩個管道。

下面分別是這兩個程式代碼,同樣是Lucy先運行,然後是Peter。

fifoLucy.c

#include <sys/types.h>
#include <sys/stat.h>

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
char write_fifo_name[] = "lucy";
char read_fifo_name[] = "peter";
int write_fd, read_fd;
char buf[256];
int len;
struct stat stat_buf;

int ret = mkfifo(write_fifo_name, S_IRUSR | S_IWUSR);
if( ret == -1)
{
printf("Fail to create FIFO %s: %s",write_fifo_name,strerror(errno));
exit(-1);
}

write_fd = open(write_fifo_name, O_WRONLY);
if(write_fd == -1)
{
printf("Fail to open FIFO %s: %s",write_fifo_name,strerror(errno));
exit(-1);
}

while((read_fd = open(read_fifo_name,O_RDONLY)) == -1)
{
sleep(1);
}

while(1)
{
printf("Lucy: ");
fgets(buf, 256, stdin);
buf[strlen(buf)-1] = '\0';
if(strncmp(buf,"quit", 4) == 0)
{
close(write_fd);
unlink(write_fifo_name);
close(read_fd);
exit(0);
}
write(write_fd, buf, strlen(buf));
len = read(read_fd, buf, 256);
if( len > 0)
{
buf[len] = '\0';
printf("Peter: %s\n", buf);
}
}
}
fifoPeter.c
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>

int main(void)
{
char write_fifo_name[] = "peter";
char read_fifo_name[] = "lucy";
int write_fd, read_fd;
char buf[256];
int len;

int ret = mkfifo(write_fifo_name, S_IRUSR | S_IWUSR);
if( ret == -1)
{
printf("Fail to create FIFO %s: %s",write_fifo_name,strerror(errno));
exit(-1);
}

while((read_fd = open(read_fifo_name, O_RDONLY)) == -1)
{
sleep(1);
}

write_fd = open(write_fifo_name, O_WRONLY);
if(write_fd == -1)
{
printf("Fail to open FIFO %s: %s", write_fifo_name, strerror(errno));
exit(-1);
}

while(1)
{
len = read(read_fd, buf, 256);
if(len > 0)
{
buf[len] = '\0';
printf("Lucy: %s\n",buf);
}
printf("Peter: ");
fgets(buf, 256, stdin);
buf[strlen(buf)-1] = '\0';
if(strncmp(buf,"quit", 4) == 0)
{
close(write_fd);
unlink(write_fifo_name);
close(read_fd);
exit(0);
}
write(write_fd, buf, strlen(buf));
}
}

[技術] Linux Semaphores PV操作程式

Written on 8:36 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/16/1531189.aspx

多程序實現PV操作,子程序為生產者,倉庫可以容納5個物品,每次檢查倉庫是否滿,若不滿則執行P操作,生產一個物品放入倉庫。父程序檢驗倉庫是否空,若不空則消耗一個物品,執行V操作。sleep函數是隨即設置生產或消費所用時間。

程式主要目的是展示Semaphores的使用,以備以後參考。

#include <stdio.h>
#include <stdlib.h>

#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>

int main()
{
int semID,ret,num;
int sleepTime;
struct sembuf buf;

semID = semget(IPC_PRIVATE,1,0666);
if(semID < 0)
{
printf("建立Semaphores出錯\n");
exit(1);
}

if(fork() == 0)
{
while(1)
{
num = semctl(semID,0,GETVAL,NULL);
if(num < 5)
{
sleepTime = rand() % 5;
sleep(sleepTime);

buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = 0;

ret = semop(semID,&buf,1);
if(ret < 0)
{
printf("執行P操作失敗!\n");
exit(1);
}

num = semctl(semID,0,GETVAL,NULL);
printf("生產者生產了一個物品放入了倉庫...倉庫中現有物品%d個\n",num);
}
}
}
else
{
while(1)
{
num = semctl(semID,0,GETVAL,NULL);
if(num > 0)
{
sleepTime = rand() % 5;
sleep(sleepTime);

buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = 0;

ret = semop(semID,&buf,1);
if(ret < 0)
{
printf("執行V操作失敗!\n");
exit(1);
}

num = semctl(semID,0,GETVAL,NULL);
printf("消費者從倉庫中消耗了一個物品...倉庫中現有物品%d個\n",num);
}
}
}

return 0;
}

[技術] Linux Message Queue程式

Written on 8:23 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/16/1531183.aspx

檔案msg為空檔案,可以為任何內容,這裡只是為了ftok函數使用。程式透過建立Message Queue,完成程序間通信,注意msgrcv的第四個參數為Message Type,他定義了從Queue中取得Message的Type。

下面是msgLucy.c,是建立Message Queue的

#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>

#define PROJID 0xFF
#define LUCY 1
#define PETER 2

int mqid;

void terminate_handler(int signo)
{
msgctl(mqid,IPC_RMID,NULL);
exit(0);
}

int main()
{
char filenm[] = "msg";
key_t mqkey;
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[256]; /* message data */
}msg;
int ret;

mqkey = ftok(filenm,PROJID);
if(mqkey == -1)
{
perror("ftok error: ");
exit(-1);
}

mqid = msgget(mqkey,IPC_CREAT | IPC_EXCL | 0666);
if(mqid == -1)
{
perror("msgget error: ");
exit(-1);
}

signal(SIGINT, terminate_handler);
signal(SIGTERM, terminate_handler);

while(1)
{
printf("Lucy: ");
fgets(msg.mtext, 256, stdin);
if (strncmp("quit", msg.mtext, 4) == 0)
{
msgctl(mqid,IPC_RMID,NULL);
exit(0);
}
msg.mtext[strlen(msg.mtext)-1] = '\0';
msg.mtype = LUCY;
msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
msgrcv(mqid,&msg,256,PETER,0);
printf("Peter: %s\n", msg.mtext);
}
}
下面的是msgPeter,是和Lucy通信的,程式先執行Lucy,再執行Peter
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>

#define PROJID 0xFF
#define LUCY 1
#define PETER 2

int main()
{
char filenm[] = "msg";
int mqid;
key_t mqkey;
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[256]; /* message data */
}msg;
int ret;

mqkey = ftok(filenm, PROJID);
if(mqkey == -1)
{
perror("ftok error: ");
exit(-1);
}

mqid = msgget(mqkey, 0);
if(mqid == -1)
{
perror("msgget error: ");
exit(-1);
}

while(1)
{
msgrcv(mqid,&msg,256,LUCY,0);
printf("Lucy: %s\n",msg.mtext);
printf("Peter: ");
fgets(msg.mtext,256,stdin);
if(strncmp("quit", msg.mtext, 4) == 0)
{
exit(0);
}
msg.mtext[strlen(msg.mtext)-1] = '\0';
msg.mtype = PETER;
msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
}
}

[技術] Linux多執行緒結束控制程式

Written on 8:20 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/05/1521170.aspx

pthread_cleanup_push和pthread_cleanup_pop是一對的。push中第一個參數是函數,第二個參數是函數的參數,pop為0表示彈出時不執行,否則再執行到pop時會執行push中指定的函數。在他們之間的程序段如果退出則執行push中指定的函數。
代碼如下:

#include <stdio.h>
#include <pthread.h>

void display()
{
printf("Hello lcrystal!\n");
}

void fun()
{
pthread_cleanup_push((void *)display,NULL);

pthread_exit(0);

pthread_cleanup_pop(0);
}

int main()
{
pthread_t id;

pthread_create(&id,NULL,(void *)fun,NULL);
pthread_join(id,NULL);

return 0;
}

[技術] Linux管線(pipe)程式

Written on 8:07 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/05/1521168.aspx

管線只能用於父子程序間通信,pipe函式用一個int[]作參數,int[] fd中fd[0]用於讀,fd[1]用於寫。代碼如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

int main()
{
int pipe1_fd[2], pipe2_fd[2];

char * parent_talks[] = {"Hi, my baby","Can you tell daddy date and time?","Daddy have to leave here, bye!",NULL};
char * child_talks[] = {"Hi, daddy","Sure,","Bye!",NULL};

char parent_buf[256], child_buf[256];
char * parent_ptr, * child_ptr;

int i,j, len;
int child_status;
time_t curtime;

if(pipe(pipe1_fd) < 0)
{
printf("pipe1 create error\n");
return -1;
}

if(pipe(pipe2_fd) < 0)
{
printf("pipe2 create error\n");
return -1;
}

if(fork() == 0) //child
{
//pipe1_fd[0] is used to read, pipe2_fd[1] is used to write
close(pipe1_fd[1]);
close(pipe2_fd[0]);

i = 0;
child_ptr = child_talks[i];
while(child_ptr != NULL)
{
len = read(pipe1_fd[0],child_buf,255);
child_buf[len] = '\0';
printf("Parent: %s\n",child_buf);

if(i == 1)
{
time(&curtime);
len = sprintf(child_buf, "%s %s", child_ptr, ctime(&curtime));
child_buf[len-1] = '\0';
write(pipe2_fd[1],child_buf,strlen(child_buf));
}
else
{
write(pipe2_fd[1],child_ptr,strlen(child_ptr));
}

child_ptr = child_talks[++i];
}

close(pipe1_fd[0]);
close(pipe2_fd[1]);
exit(0);
}
else //parent
{
//pipe1_fd[1] is used to write, pipe2_fd[0] is used to read
close(pipe1_fd[0]);
close(pipe2_fd[1]);

j = 0;
parent_ptr = parent_talks[j];
while(parent_ptr != NULL)
{
write(pipe1_fd[1],parent_ptr,strlen(parent_ptr));
len = read(pipe2_fd[0],parent_buf,255);
parent_buf[len] = '\0';
printf("Child: %s\n", parent_buf);
parent_ptr = parent_talks[++j];
}

close(pipe1_fd[1]);
close(pipe2_fd[0]);
wait(&child_status);
exit(0);
}
}

[技術] Linux攔截Signal程式

Written on 7:57 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/05/1521159.aspx

攔截鍵盤Ctrl + C,程式執行後,進入while(1)迴圈,當按下Ctrl + C後,執行signal函數指定的函式fun,輸出字串。
代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void fun(int signal)
{
printf("Hello World!\n");
exit(0);
}

int main()
{
signal(SIGINT,fun);

while(1)
{
;
}
}

[技術] Linux Shared Memory程式

Written on 7:40 下午 by Yu Lai

From: http://blog.csdn.net/lcrystal623/archive/2007/03/05/1521158.aspx

要運行程式,需要在當前目錄下建立一個share檔案,share是一個空檔案,沒有任何意義,只是函數ftok需要一個檔案名作參數,ftok另一個參數可以為任何數字。

程式執行後,分為父子程序,子程序申請Shared Memory,然後等待父程序繼續執行,父程序首先等待子程序申請到Shared Memory區段(segment),然後輸出Shared Memory中的內容,為了演示Shared Memory可以隨時更新,程序中在子程序裡產生隨機數寫入Shared Memory供父程序讀取。
代碼如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <time.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

int shmID;
char * buf;

void finalize(int signo)
{
shmctl(shmID,IPC_RMID,NULL);

exit(0);
}

int main()
{
int i = 1;
key_t shmKey;

signal(SIGINT,finalize);
signal(SIGTERM,finalize);

if(fork() == 0) //子程序
{
shmKey = ftok("share",16); //可以使用任何大於0的數字,如果名字和數相同,則產生的key相同。
if(shmKey == -1)
{
printf("建立key出錯\n");
exit(-1);
}

shmID = shmget(shmKey,20,IPC_CREAT | IPC_EXCL | 0666);
if(shmID == -1)
{
printf("建立Shared Memory Segment出錯\n");
exit(-1);
}

sleep(2); //等待父程序執行,好顯示第一行為空。
while(1)
{
buf = (char *)shmat(shmID,NULL,0);
srandom(time(NULL));
sprintf(buf,"%d",random());
shmdt(buf);
}
}
else //父程序
{
sleep(1); //讓子程序先執行,以建立Shared Memory Segment。

shmKey = ftok("share",16); //可以使用任何大於0的數字,如果名字和數相同,則產生的key相同。
if(shmKey == -1)
{
printf("建立key出錯\n");
exit(-1);
}

shmID = shmget(shmKey,20,0); //0表示如果shmKey映射的不存在則報錯。
if(shmID == -1)
{
printf("引用Segment id出錯\n");
exit(-1);
}

while(1)
{
buf = (char *)shmat(shmID,NULL,0);
printf("%d. 現在Shared Memory中的內容為:%s\n",i++,buf);
shmdt(buf);
sleep(1);
}
}

return 0;
}

[技術] Linux多執行緒程式

Written on 7:20 下午 by Yu Lai

整個程式並沒有體現出對共享資源鎖的保護使用,只是個簡單的例子,原理完全正確,但由於簡單,CPU運行一定會順利的執行,因此不加鎖結果也相同。主要目的是演示如何建立執行緒,如何建立mutex實現共享鎖。代碼如下:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>

int resource = 0;
pthread_mutex_t mutex;

void handle()
{
int i;

for(i = 0; i < 10; i++)
{
pthread_mutex_lock(&mutex);

resource++;

pthread_mutex_unlock(&mutex);
sleep(1);
}
}

int main()
{
pthread_t id;
int i ;
int ret;

ret = pthread_mutex_init(&mutex,NULL);
if(ret != 0)
{
printf("創建共享鎖失敗!\n");
exit(1);
}

ret = pthread_create(&id,NULL,(void *)handle,NULL);
if(ret != 0)
{
printf("創建線程出錯!\n");
exit(1);
}

for(i = 0; i < 10; i++)
{
pthread_mutex_lock(&mutex);

printf("%d\n",resource);

pthread_mutex_unlock(&mutex);
sleep(2);
}

pthread_join(id,NULL);
pthread_mutex_destroy(&mutex);

return (0);
}

[技術] 使用AutoMake輕鬆生成Makefile

Written on 5:31 下午 by Yu Lai

From: http://www.itepub.net/html/kaifawendang/caozuoxitong/Linux/bianchengkaifa/2006/0501/14712.html

摘要:
在 Unix 上寫過程序的人一般都遇到過 Makefile,尤其是用 C 來開發程序的人。用 make 來開發和編譯程序的確很方便,可是要寫出一個MakeFile就不那麼簡單了。偏偏介紹 Makefile 的文件不多,GNU Make 那份印出來要幾百頁的文件,光看完 Overview 自己就快要先Over了,難怪許多人聞 Unix色變。本文將介紹如何利用 GNU Autoconf 及 Automake 這兩套軟件來幫助『自動』產生 Makefile 文件,並且讓開發出來的的軟件可以像 Apache, MySQL 和常見的 GNU 軟件一樣,只要會 ``./configure'', ``make'', ``make install'' 就可以把程序安裝到系統中。如果您有心開發 Open Source 的軟件,或只是想在 Unix 系統下寫寫程序。希望這份介紹文件能幫助您輕鬆的進入 Unix Programming 的殿堂。


1. 簡介
Makefile 基本上就是『目標』(target), 『關聯』(dependencies) 和『動作』三者所組成的一系列規則。而 make 就會根據 Makefile 的規則來決定如何編譯 (compile) 和連接 (link) 程式。實際上,make 可做的不只是編譯和連接程序,例如 FreeBSD 的 port collection 中,Makefile還可以做到自動下載遠程程序,解壓縮 (extract) , 打補丁 (patch),設定,然後編譯,安裝到系統中。
Makefile 基本結構雖然很簡單,但是妥善運用這些規則就可以變換出許多不同的花樣。卻也因為這樣,許多剛剛開始學習寫Makefile 時會覺得沒有規範可以遵循,每個人寫出來的Makefile都不大一樣,不知道從哪裡下手,而且常常會受到自己的開發環境的限制,只要環境參數不同或者路徑更改,可能 Makefile 就得跟著修改修改。雖然有 GNU Makefile Conventions (GNU Makefile慣例例)訂出一些使用 GNU 程式設計時撰寫 Makefile 的一些標準和規範,但是內容很長而且很複雜,並且經常作一些調整,為了減輕程序開發人員維護Makefile 的負擔,因此出現了Automake。
程序設計者只需要寫一些預先定義好的巨集 (macro),提交給Automake處理後會產生一個可以供 Autoconf 使用的 Makefile.in文件。再配合利用 Autoconf產生的自動培植設置文件 configure 即可產生一份符合符合 GNU Makefile 慣例的 Makeifle 了。

2. 上路之前
在開始使用 Automake 之前,首先確認你的系統安裝有如下軟件:

1. GNU Automake
2. GNU Autoconf
3. GNU m4
4. perl
5. GNU Libtool (如果你需要產生 shared library)
建議最好也使用 GNU C/C++ 編譯器 、GNU Make 以及其它 GNU 的工具程序來作為開發的環境,這些工具都是屬於 Open Source Software 不但免費而且功能強大。如果你是使用 Red Hat Linux 可以找到所有上述軟件的 rpm 文件,FreeBSD 也有現成的 package 可以直接安裝,或也可以自行下載這些軟件的源代碼回來安裝。下面的示例是在Red Hat Linux 5.2 + CLE2 的環境下所完成的。

3. 一個簡單的例子 Automake 所產生的 Makefile 除了可以做到程式的編譯和連接,也已經把如何產生程序文件 (如 manual page, info 文件及 dvi 文件) 的動作,還有把源碼文件包裝起來以供發佈都考慮進去了,所以程序源代碼所存放的目錄結構最好符合GNU 的標準慣例,接下來就用一個hello.c 來做為例子。
在工作目錄下建立一個新的子目錄"devel"',再在 devel 下建立一個"hello"' 的子目錄,這個目錄將作為存放 hello這個程序及其相關文件的地方:
% mkdir devel
% cd devel
% mkdir hello
% cd hello 用編輯器寫一個hello.c文件,

#include
int main(int argc, char** argv) {
printf(``Hello, GNU!n'');
return 0;
}

接下來就要用 Autoconf 及 Automake 來產生 Makefile 文件了,
1. 用 autoscan 產生一個 configure.in 的原型,執行autoscan 後會產生一個configure.scan 的文件,可以用它作為 configure.in文件的藍本。
% autoscan
% ls configure.scan hello.c

2 . 編輯 configure.scan文件,如下所示,並且改名為configure.in
dnl Process this file with autoconf to produce a configure script.
AC_INIT(hello.c)
AM_INIT_AUTOMAKE(hello, 1.0)
dnl Checks for programs.
AC_PROG_CC
dnl Checks for libraries.
dnl Checks for header files.
dnl Checks for typedefs, structures, and compiler characteristics.
dnl Checks for library functions.
AC_OUTPUT(Makefile)

3. 執行 aclocal 和 autoconf ,分別會產生 aclocal.m4 及 configure 兩個文件
% aclocal
% autoconf
% ls aclocal.m4 configure configure.in hello.c

4. 編輯 Makefile.am 文件,內容如下
AUTOMAKE_OPTIONS= foreign
bin_PROGRAMS= hello
hello_SOURCES= hello.c

5. 執行 automake --add-missing ,Automake 會根據Makefile.am 文件產生一些文件,包含最重要的 Makefile.in
% automake --add-missing automake: configure.in: installing `./install-sh' automake: configure.in: installing `./mkinstalldirs' automake: configure.in: installing `./missing'

6. 最後執行 ./configure ,
% ./configure creating cache ./config.cache checking for a BSD compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking whether make sets ${MAKE}... yes checking for working aclocal... found checking for working autoconf... found checking for working automake... found checking for working autoheader... found checking for working makeinfo... found checking for gcc... gcc checking whether the C compiler (gcc ) works... yes checking whether the C compiler (gcc ) is a cross-compiler... no checking whether we are using GNU C... yes checking whether gcc accepts -g... yes updating cache ./config.cache creating ./config.status creating Makefile

現在你的目錄下已經產生了一個 Makefile 檔,下個 ``make'' 指令就可以開始編譯 hello.c 成執行檔,執行 ./hello 和 GNU 打聲招呼吧!
% make gcc -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -c hello.c gcc -g -O2 -o hello hello.o 
% ./hello Hello! GNU!

你還可以試試 ``make clean'',''make install'',''make dist'' 看看會有什麼結果。你也可以把產生出來的 Makefile 秀給你的老闆,讓他從此對你刮目相看 :-)

4. 追根問底
上述產生Makefile 的過程和以往自行編寫的方式非常不一樣,捨棄傳統自定義make 的規則,使用 Automake 只需用到一些已經定義好的巨集就可以了。我們把巨集及目標 (target)寫在Makefile.am 文件內,Automake 讀入 Makefile.am 文件後會把這一串已經定義好的巨集展開並產生相對應的 Makefile.in 文件,然後再由 configure這個 shell script 根據 Makefile.in 產生合適的Makefile。
[Figure 1:利用 autoconf 及 automake產生Makefile 的流程]
上圖表示在上一範例中要使用的文件檔案及產生出來的文件,有星號 (*) 者代表可執行文件。在此示例中可由 Autoconf 及 Automake 工具所產生的額外文件有 configure.scan、aclocal.m4、configure、Makefile.in,需要自行加入設置的有configure.in 及 Makefile.am。
4.1 編輯 configure.in 文件 Autoconf 是用來產生 'configure'文件的工具。'configure' 是一個 shell script,它可以自動設定原始程序以符合各種不同平台上Unix 系統的特性,並且根據系統參數及環境產生合適的Makefile文件或C 的頭文件(header file),讓原始程式可以很方便地在不同的平台上進行編譯。Autoconf會讀取 configure.in 文件然後產生'configure' 這個 shell script。
configure.in 文件內容是一系列GNU m4 的巨集,這些巨集經autoconf處理後會變成檢查系統特性的shell scripts。 configure.in 內巨集的順序並沒有特別的規定,但是每一個configure.in 文件必須在所有巨集前加入 AC_INIT 巨集,然後在所有巨集的最後加上 AC_OUTPUT 巨集。可先用 autoscan 掃瞄原始文件以產生一個 configure.scan 文件,再對 configure.scan 做些修改成 configure.in 文件。在範例中所用到的巨集如下:
dnl 這個巨集後面的字不會被處理,可以視為註釋 AC_INIT(FILE) 該巨集用來檢查源代碼所在路徑,autoscan 會自動產生,一般無須修改它。 AM_INIT_AUTOMAKE(PACKAGE,VERSION) 這個是使用 Automake 所必備的巨集,PACKAGE 是所要產生軟件套件的名稱,VERSION 是版本編號。 AC_PROG_CC 檢查系統可用的C編譯器,若源代碼是用C寫的就需要這個巨集。 AC_OUTPUT(FILE) 設置 configure 所要產生的文件,若是Makefile ,configure 便會把它檢查出來的結果帶入 Makefile.in 文件後產生合適的 Makefile。 實際上,這裡使用 Automake 時,還需要一些其他的巨集,這些額外的巨集我們用 aclocal來幫助產生。執行 aclocal會產生aclocal.m4 文件,如果無特別的用途,可以不需要修改它,用 aclocal 所產生的巨集會告訴 Automake如何動作。
有了 configure.in 及 aclocal.m4兩個文件以後,便可以執行 autoconf來產生 configure 文件了。
4.2 編輯Makefile.am 文件 接下來要編輯Makefile.am 文件,Automake 會根據 configure.in 中的巨集把Makefile.am 轉成 Makefile.in 文件。 Makefile.am 文件定義所要產生的目標:
AUTOMAKE_OPTIONS 設置 automake 的選項。Automake 主要是幫助開發 GNU 軟件的人員來維護軟件,所以在執行 automake 時,會檢查目錄下是否存在標準 GNU 軟件中應具備的文件,例如 'NEWS'、'AUTHOR'、'ChangeLog' 等文件。設置 foreign 時,automake 會改用一般軟件的標準來檢查。 bin_PROGRAMS 定義要產生的執行文件名。如果要產生多個執行文件,每個文件名用空白符隔開。 hello_SOURCES 定義 'hello' 這個執行程序所需要的原始文件。如果 'hello'這個程序是由多個原始文件所產生,必須把它所用到的所有原始文件都列出來,以空白符隔開。假設 'hello' 還需要 'hello.c'、'main.c'、'hello.h' 三個文件的話,則定義 hello_SOURCES= hello.c main.c hello.h 如果定義多個執行文件,則對每個執行程序都要定義相對的filename_SOURCES。
編輯好 Makefile.am 文件,就可以用 automake --add-missing來產生 Makefile.in。加上 --add-missing 選項來告訴 automake順便假如包裝一個軟件所必須的文件。Automake產生生出來的 Makefile.in 文件是完全符合 GNU Makefile 的慣例,只要執行 configure這個shell script 便可以產生合適的 Makefile 文件了。
4.3 使用 Makefile 利用 configure 所產生的 Makefile文件有幾個預先設定的目標可供使用,這裡只用幾個簡述如下:
make all 產生設定的目標,既次範例中的執行文件。只敲入make 也可以,此時會開始編譯源代碼,然後連接並產生執行文件。 make clean 清除之前所編譯的執行文件及目標文件(object file, *.o)。 make distclean 除了清除執行文件和目的文件以外,也把 configure 所產生的 Makefile 清除掉。 make install 將程序安裝到系統中,若源碼編譯成功,且執行結果正確,便可以把程序安裝到系統預先設定的執行文件存放路徑中,若用 bin_PROGRAMS 巨集的話,程序會被安裝到 /usr/local/bin下。 make dist 將程序和相關的文檔包裝為一個壓縮文檔以供發佈 (distribution) 。執行完在目錄下會產生一個以PACKAGE-VERSION.tar.gz 為名稱的文件。PACKAGE 和 VERSION 這兩個參數是根據 configure.in 文件中 AM_INIT_AUTOMAKE(PACKAGE, VERSION) 的定義。在此範例中會產生 'hello-1.0.tar.gz' 的文件。 make distcheck 和 make dist 類似,但是加入檢查包裝以後的壓縮文件是否正常,這個目標除了把程序和相關文檔包裝成 tar.gz 文件外,還會自動把這個壓縮文件解開,執行 configure,並執行 make all ,確認編譯無錯誤以後,戶顯示這個 tar.gz 文件已經準備好可以發佈了。這個檢查非常有用,檢查過關的套件,基本上可以給任何具備 GNU 開發環境的人去重新編譯成功。就 hello-1.tar.gz 這個範例而言,除了在Red Hat Linux 上,在 FreeBSD 2.2.x 也可以正確編譯。 要注意的是,利用 Autoconf 及 Automake 所產生出來的軟件套件是可以在沒有安裝 Autoconf 及 Automake 的環境使用的,因為 configure 是一個 shell script,它己被設計為可以在一般 Unix 的 sh 這個 shell 下執行。但是如果要修改 configure.in 及 Makefile.am 文件再產生新的 configure 及 Makefile.in 文件時就一定要有 Autoconf 及 Automake 了。

5. 相關資料
Autoconf 和 Automake 功能十分強大,可以從它們附帶的 info 穩當4中找到詳細的使用方法說明。你也可以從許多現有的GNU 軟件或 Open Source 軟件中找到相關的 configure.in 或 Makefile.am 文件,他們是學習 Autoconf 及 Automake 更多技巧的最佳範例。
這個簡介只用到了 Autoconf 及 Automake 的皮毛罷了,如果你有心加入 Open Source 軟件開發的行列,希望這篇文章可以幫助你對產生 Makefile 有個簡單的瞭解。其它有關開發 GNU 程式或 C 程序設計及 Makefile 的詳細運用及技巧,建議從 GNU Coding Standards (GNU 編碼規定) 讀起,裡面包含了 GNU Makefile 慣例,及開發 GNU 軟件的標準程序和慣例。這些 GNU 軟件的在線說明文件可以在 http://www.gnu.org/ 上找到。

6. 結束語
利用 Autoconf 及 Automake,產生一個 Makefile 似乎不再像以前那麼困難了,而使用 Autoconf 也使得我們在不同平台上或各家 Unix 之間發佈及便宜程序變的簡單,這對於在Unix 系統上程序開發員來說減輕了許多負擔。妥善運用這些 GNU 的工具軟件,可以幫助我們更容易的去開發程序,而且更容易維護源代碼。

[工具] Source Insight with Artistic Style

Written on 5:18 下午 by Yu Lai

Windows平台下也有好多人都喜歡用SourceInsight編輯C/C++程序,但是SourceInsight沒有提供對代碼格式化的功能,如果將Artistic Style集成到SourceInsight中,那就可以為它擴展出代碼格式化的功能了。

假定AStyle.exe的目錄是"C:\ArtisticStyle\"。下面簡要地介紹下Artistic Style集成到SourceInsight中的方法。
1. 打開你的SourceInsight, 選擇菜單"Options-->Custom Commands-->Add", 輸入Artistic Style(可以隨便輸入一個名字)。
2. Run中輸入: C:\ArtisticStyle\Astyle.exe --style=linux --indent=tab %f
3. Dir留空,將Iconic Window, Capture Output, Parse Links in OutPut, File,then Line 四項前打上勾。
4. 然後點對話框中右側的按鈕“Menu”, Menu--->Menu-->View-->, 右側Insert, OK.
5. 此時在SourceInsight中的View菜單下多了個Style的子菜單選項,可以用它來對單個C/C++文件進行格式化。

[技術] uClinux上Berkeley DB v4.5.20移植手記

Written on 4:01 下午 by Yu Lai

轉載自: http://blog.iyi.cn/hily/archives/2006/10/uclinuxberkeley_db_v4520.html

Berkeley DB 是一個很棒的開源的嵌入式數據庫,它主要運行在Unix/Linux上。現在它已成為Oracle的一部分,叫作Oracle Berkeley DB
下面主要介紹一下它在我最近玩的uClinux上的移植過程。

Hily Jiang
Email&Gtalk: hilyjiang at Gmail
Blog: http://hily.iyi.cn/

下載頁面:
http://dev.sleepycat.com/downloads/releasehistorybdb.html

(一)解壓

linux:/home/work/db # tar -zxf db-4.5.20.tar.gz
linux:/home/work/db # cd db-4.5.20/
linux:/home/work/db/db-4.5.20 # ls
. db_checkpoint db_upgrade libdb_java README
.. db_deadlock db_verify LICENSE rep
btree db_dump dist lock repmgr
build_unix db_dump185 docs log rpc_client
build_vxworks db_hotbackup env mod_db4 rpc_server
build_windows dbinc examples_c mp sequence
clib dbinc_auto examples_cxx mutex tcl
common db_load examples_java os test
crypto dbm fileops os_vxworks txn
cxx db_printlog hash os_windows xa
db db_recover hmac perl
db185 dbreg hsearch php_db4
db_archive db_stat java qam

docs目錄下有我們需要的文檔,包括快速入門、各種語言的API手冊等資料。

(二)配置和編譯
建立一個腳本以方便配置。由於unix/linux編譯時的工作路徑必須是build_unix,因此我們需要在build_unix目錄下創建腳本。
我創建了一個名為myconfig的腳本,內容如下:

1 #!/bin/sh
2
3 CC=arm-elf-gcc \
4 CFLAGS="-Os -D__uClinux__ -fno-builtin -I/home/uClinux-dist/linux-2.4.x/include -I/home/uClinux-dist/lib/uClibc/include -I/home/uClinux-dist/lib/uClibc/include/../ " \
5 LDFLAGS="-Wl,-elf2flt -Wl,-move-rodata -L/home/uClinux-dist/lib/uClibc/lib -L/home/uClinux-dist/lib/uClibc/lib/../ -lc " \
6 ../dist/configure \
7 --prefix=/home/work/db/Berkeley.DB \
8 --build=i686-linux \
9 --host=arm-elf-linux \
10 --disable-cryptography \
11 --disable-hash \
12 --disable-queue \
13 --disable-replication \
14 --disable-statistics \
15 --disable-verify \
16 --disable-compat185 \
17 --disable-cxx \
18 --disable-debug \
19 --disable-debug_rop \
20 --disable-debug_wop \
21 --disable-diagnostic \
22 --disable-dump185 \
23 --disable-java \
24 --disable-mingw \
25 --disable-o_direct \
26 --disable-posixmutexes \
27 --disable-pthread_api \
28 --disable-rpc \
29 --disable-smallbuild \
30 --disable-tcl \
31 --disable-test \
32 --disable-uimutexes \
33 --enable-umrw \
34 --disable-shared \
35 --enable-static \
36 --enable-fast-install \
37 --disable-libtool-lock \
38 --disable-largefile

關於configure配置參數的含義,可以運行"../dist/configure --help"查看幫助,這裡不再介紹。
要強調的一點是uClibc只能用靜態編譯,因此一定要選擇--disable-shared。
接著執行./myconfig運行配置並編譯安裝函數庫:
linux:/home/work/db/db-4.5.20/build_unix # ./myconfig >/dev/null
configure: WARNING: In the future, Autoconf will not detect cross-tools
whose name does not start with the host triplet. If you think this
configuration is useful to you, please write to autoconf@gnu.org.
cat: /etc/ld.so.conf.d/*.conf: No such file or directory
linux:/home/work/db/db-4.5.20/build_unix # make >/dev/null && make install >/dev/null
../dist/../hmac/sha1.c:96: warning: `R0' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:40: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:97: warning: `R1' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:42: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:98: warning: `R2' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:44: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:99: warning: `R3' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:46: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:100: warning: `R4' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:48: warning: this is the location of the previous definition
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
strip: /home/work/db/Berkeley.DB/bin/db_archive: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_checkpoint: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_deadlock: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_dump: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_hotbackup: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_load: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_printlog: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_recover: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_stat: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_upgrade: 不可識別的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_verify: 不可識別的文件格式

編譯過程中會出現一些warning,不用理它們。
安裝完後,會在指定的安裝目錄/home/work/db/Berkeley.DB下生成函數庫:
linux:/home/work/db/db-4.5.20/build_unix # cd /home/work/db/Berkeley.DB/
linux:/home/work/db/Berkeley.DB # ls
. .. bin docs include lib
linux:/home/work/db/Berkeley.DB # ll lib/
總用量 1962
drwxr-xr-x 2 root root 104 2006-10-08 12:57 .
drwxr-xr-x 6 root root 144 2006-10-08 12:57 ..
-rw-r--r-- 1 root root 1002266 2006-10-08 12:57 libdb-4.5.a
-rw-r--r-- 1 root root 1002266 2006-10-08 12:57 libdb.a


(三)數據庫操作測試
創建一個測試程序如下:
linux:/home/work/db/Berkeley.DB # cat -n testdb.c
1 #include
2 #include
3 #include
4
5 #define DESCRIPTION_SIZE 20
6 int main()
7 {
8 DB *dbp; /* DB structure handle */
9 u_int32_t flags; /* database open flags */
10 int ret; /* function return value */
11 char *description = "Grocery bill.";
12 char *description1[DESCRIPTION_SIZE + 1];
13 DBT key, data;
14 float money;
15
16 /* Initialize the structure. This
17 * database is not opened in an environment,
18 * so the environment pointer is NULL. */
19 ret = db_create(&dbp, NULL, 0);
20 if (ret != 0) {
21 /* Error handling goes here */
22 printf("Create fail!\n");
23 }
24
25 /* Database open flags */
26 flags = DB_CREATE; /* If the database does not exist,
27 * create it.*/
28
29 /* open the database */
30 ret = dbp->open(dbp, /* DB structure pointer */
31 NULL, /* Transaction pointer */
32 "/home/my_db.db", /* On-disk file that holds the database. */
33 NULL, /* Optional logical database name */
34 DB_BTREE, /* Database access method */
35 flags, /* Open flags */
36 0); /* File mode (using defaults) */
37 if (ret != 0) {
38 /* Error handling goes here */
39 printf("Created new database.\n");
40 }
41
42 money = 122.45;
43
44 /* Zero out the DBTs before using them. */
45 memset(&key, 0, sizeof(DBT));
46 memset(&data, 0, sizeof(DBT));
47
48 key.data = &money;
49 key.size = sizeof(float);
50
51 data.data = description;
52 data.size = strlen(description) +1;
53
54 ret = dbp->put(dbp, NULL, &key, &amp;amp;amp;data, DB_NOOVERWRITE);
55 if (ret == DB_KEYEXIST) {
56 dbp->err(dbp, ret,
57 "Put failed because key %f already exists", money);
58 }
59
60 memset(&data, 0, sizeof(DBT));
61
62 data.data = &description1;
63 data.ulen = DESCRIPTION_SIZE + 1;
64 data.flags = DB_DBT_USERMEM;
65 dbp->get(dbp, NULL, &key, &amp;amp;amp;data, 0);
66
67 printf("data: %s\n", (char *)data.data);
68
69 /* When we're done with the database, close it. */
70 if (dbp != NULL)
71 dbp->close(dbp, 0);
72
73 return 0;
74 }

這個程序會在目標板上/home/目錄下創建一個文件名為my_db.db的數據庫,接著增加一條記錄,然後從數據庫中讀取出新添加的這條記錄,最後關閉數據庫。
程序要燒寫到目標板上,需要進行交叉編譯:
linux:/home/work/db/Berkeley.DB # arm-elf-gcc -O3 -Wall -mapcs-32 -mtune=arm7tdmi -fno-builtin -msoft-float -Os -D__uClinux__ -D__ARM_CPU__ -I/home/work/uClinux-dist/lib/uClibc/include -I/home/uClinux-dist/linux-2.4.x/include -I/home/work/db/Berkeley.DB/include -D_DEBUG_ -c testdb.c -o testdb.o
linux:/home/work/db/Berkeley.DB # arm-elf-gcc testdb.o -nostartfiles -Wl, -elf2flt -L/home/uClinux-dist/lib/uClibc/lib -L/home/work/db/Berkeley.DB/lib /home/uClinux-dist/lib/uClibc/lib/crt0.o /home/uClinux-dist/lib/uClibc/lib/crti.o /home/uClinux-dist/lib/uClibc/lib/crtn.o -lc -ldb -o testdb
linux:/home/work/db/Berkeley.DB # ll
總用量 1217
drwxr-xr-x 6 root root 280 2006-10-08 13:22 .
drwxr-xr-x 4 root root 144 2006-10-08 12:50 ..
drwxr-xr-x 2 root root 376 2006-10-08 12:57 bin
drwxr-xr-x 14 root root 384 2006-10-08 12:57 docs
drwxr-xr-x 2 root root 96 2006-10-08 12:57 include
drwxr-xr-x 2 root root 104 2006-10-08 12:57 lib
-rwxr--r-- 1 root root 584476 2006-10-08 13:22 testdb
-rw-r--r-- 1 root root 2171 2006-10-08 13:22 testdb.c
-rw------- 1 root root 2163 2006-10-08 13:09 testdb.c~
-rwxr-xr-x 1 root root 673683 2006-10-08 13:22 testdb.gdb
-rw-r--r-- 1 root root 1540 2006-10-08 13:22 testdb.o

生成的可執行文件比較大,將近600KB。
燒寫到目標機上後運行,結果如下:
# /home/testdb
data: Grocery bill.

搞定~
總的來說,Berkeley DB還是比較易用的。就是生成的可執行文件還是太大些 :-(

[技術] 嵌入式系統入學測驗

Written on 10:19 上午 by Yu Lai

原文出處 A 'C' Test: The 0x10 Best Questions for Would-be Embedded Programmers: http://www.embedded.com/2000/0005/0005feat2.htm

以下是網路上找到中文版,作者不詳。

C語言測試是徵選嵌入式系統程式員過程中必須而且有效的方法。這些年我既參加也組織了許多這種測試,在這過程中我意識到這些測試能為面試者和被面試者提供許多有用訊息,此外,撇開面試的壓力不談,這種測試也是相當有趣的。

從被面試者的角度來講,你能了解許多關於出題者或監考者的情況。這個測試只是出題者為顯示其對ANSI標準細節的知識而不是技術技巧而設計嗎?這個愚蠢的問題嗎?如要你答出某個字符的ASCII值。這些問題著重考察你的系統調用(invoke)和記憶體分發策略方面的能力嗎?這反映出出題者也許花時間在微處理機上而不在嵌入式系統上。

如果上述任何問題的答案是“是”的話,那麼我知道我得認真考慮我是否應該去做這份工作。

從面試者的角度來講,一個測試也許能從多方面揭示應試者的素質。最基本的,你能了解應試者C語言的水準。不管怎麼樣,看一下這人如何回答他不會的問題也是滿有趣。應試者是以好的直覺做出明智的選擇,還是只是瞎蒙呢?當應試者在某個問題上卡住時是找藉口呢,還是表現出對問題的真正的好奇心,把這看成學習的機會呢?我發現這些訊息與他們的測試成績一樣有用。

有了這些想法,我決定出一些真正針對嵌入式系統的考題,希望這些令人頭痛的考題能給正在謀職的人一點幫住。這些問題都是我這些年實際碰到的。其中有些題很難,但它們應該都能給你一點啟發。

這個測試適用於不同水準的應試者,大多數初級水準的應試者的成績會很差,經驗豐富的程式員應該有很好的成績。為了讓你能自己決定某些問題的偏好,每個問題沒有分發分數,如果選擇這些考題為你所用,請自行按你的意思分發分數。


==============================================


預處理器 (Preprocessor)

1.用預處理指令#define 聲明一個常數,用以表示1年中有多少秒 (忽略閏年問題)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在這想看到幾件事情︰

a.#define 語法的基本知識 (例如︰不能以分號結束,括號的使用,等等)
b.懂得預處理器將為你計算常數表達式的值,因此,直接寫出你是如何計算一年中有多少秒會比直接計算出實際的值更清晰。
c.意識到這個表達式將使一個16位元的機器產生整數型溢位 - 因此要用到長整型符號L,告訴編譯器這個常數是的長整型數。
d.如果你在你的表達式中用到UL (表示無符號長整型) ,那麼你有了一個好的起點。記住,第一印象很重要。

2.寫一個“標準”巨集MIN ,這個巨集輸入兩個參數並返回較小的一個。

#define MIN(A, B) ((A) <= (B) ? (A) : (B))
這個測試是為下面的目的而設的︰

a.標識#define在巨集中應用的基本知識。這是很重要的,因為在行內(inline)運算子變為標準C的一部分之前,巨集是方便產生行內程式碼的唯一方法,對於嵌入式系統來說,為了能達到要求的性能,行內程式碼經常是必須的方法。
b.三元運算子的知識。這個運算子存在C語言中的原因是它使得編譯器能產生比if-then-else更優化的程式碼,了解這個用法是很重要的。
c.懂得在巨集中小心地把參數用括號括起來。我也用這個問題開始討論巨集的副作用,例如︰當你寫下面的程式碼時會發生什麼事?

least = MIN(*p++, b);

3.預處理器標識#error的目的是什麼?

如果你不知道答案,請看參考文獻1。這問題對區分一個正常的伙計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種問題的答案。當然如果你不是在找一個書呆子,那麼應試者最好希望自己不要知道答案。


==============================================


無窮迴圈 (Infinite loops)

4.嵌入式系統中經常要用到無窮迴圈,你怎麼樣用C編寫無窮迴圈呢?

這個問題用幾個解決方案。我首選的方案是︰

while(1)
{
...
}
一些程式員更喜歡如下方案︰
for(;;)
{
...
}
這個實作方式讓我為難,因為這個語法沒有確切表達到底怎麼回事。如果一個應試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的基本原理。如果他們的基本答案是︰“我被教著這樣做,但從沒有想到過為什麼。”這會給我留下一個壞印象。
第三個方案是用 goto︰
Loop:
...
goto Loop;
應試者如給出上面的方案,這說明或者他是一個組合語言程式員 (這也許是好事) 或者他是一個想進入新領域的BASIC/FORTRAN程式員。


==============================================


數據宣告 (Data declarations)

5.用變數a給出下面的定義:
a)一個整型數 (An integer)
b)一個指向整數的指標 (A pointer to an integer)
c)一個指向指標的指標,它指向的指標是指向一個整型數 (A pointer to a pointer to an integer)
d)一個有10個整數型的陣列 (An array of 10 integers)
e)一個有10個指標的陣列,該指標是指向一個整數型的 (An array of 10 pointers to integers)
f)一個指向有10個整數型陣列的指標 (A pointer to an array of 10 integers)
g)一個指向函數的指標,該函數有一個整數型參數並返回一個整數 (A pointer to a function that takes an integer as an argument and returns an integer)
h)一個有10個指標的陣列,該指標指向一個函數,該函數有一個整數型參數並返回一個整數 (An array of ten pointers to functions that take an integer argument and return an integer)


答案是︰

a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
人們經常聲稱這裡有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間裡,我確定我知道這個問題的答案。應試者如果不知道所有的答案(或至少大部分答案),那麼也就沒有為這次面試做準備,如果該面試者沒有為這次面試做準備,那麼他又能為什麼出準備呢?


==============================================


Static

6.關鍵字static的作用是什麼?

這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用︰
a.在函數本體內(in Function Block),一個被宣告為靜態的變數,在這一函數被呼叫過程中維持其值不變。
b.在一個Block(ie. {...} )內 (但在函數體外),一個被宣告為靜態的變數可以被Block內所有的函數存取, 但不能被Block外的其它函數存取。它是一個本地的全局變量。
c.在Block內,一個被聲明為靜態的函數,只可被這一Block內的其它函數呼叫。也就是這個函數被限制在宣告它的Block的本地範圍內使用。大多數應試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,因為他顯然不懂得本地化資料和程式碼範圍的好處和重要性。


==============================================


Const(常量)

7.關鍵字const有什麼含意?

我只要一聽到被面試者說︰“const意味著常數”,我就知道我正在和一個業餘者打交道。去年Dan Saks已經在他的文章裡完全概括了const的所有用法,因此ESP(譯者︰Embedded Systems Programming)的每一位讀者應該非常熟悉const能做什麼和不能做什麼。如果你從沒有讀到那篇文章,只要能說出const意味著“只讀”就可以了。儘管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細的答案,仔細讀一下Saks的文章吧。)如果應試者能正確回答這個問題,我將問他一個附加的問題︰下面的聲明都是什麼意思?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前兩個的作用是一樣,a是一個常數型整數。
第三個意味著a是一個指向常數型整數的指標(也就是,整型數是不可修改的,但指標可以)。
第四個意思a是一個指向整數的常數型指標(也就是說,指標指向的整數是可以修改的,但指標是不可修改的)。
最後一個意味著a是一個指向常數型整數的常數型指標(也就是說,指標指向的整數是不可修改的,同時指標也是不可修改的)。
如果應試者能正確回答這些問題,那麼他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關鍵字 const,也還是能很容易寫出功能正確的程式,那麼我為什麼還要如此看重關鍵字const呢 ?我有以下的幾個理由︰
a。關鍵字const的作用是給讀你程式碼的人傳達非常有用的訊息,實際上,宣告一個參數為常量是為了告訴了程式員這個參數的應用目的。如果你曾花很多時間清理其它人留下的垃圾(記憶體回收),你就會很快學會感謝這點多餘的訊息。(當然,懂得用const的程式員很少會留下的垃圾讓別人來清理的)
b。透過給優化器一些附加的訊息,使用關鍵字const也許能產生更緊湊的程式碼。
c。合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的程式碼修改。簡而言之,這樣可以減少bug的出現。


==============================================


Volatile(易變的)

8. 關鍵字volatile有什麼含意?並給出三個不同的例子。

一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在暫存器裡的備份。下面是volatile變量的幾個例子︰
a.並行設備的硬體暫存器 (如︰狀態暫存器)
b.一個中斷服務次程序中會訪問到的非自動變數(Non-automatic variables)
c.多執行緒應用中被幾個任務(task)共享的變數
回答不出這個問題的人是不會被僱佣的。(os:還好我不是讓這個人面試.....)我認為這是區分C程式員和嵌入式系統程式員的最基本的問題。搞嵌入式的家伙們經常同硬體、中斷、RTOS等等打交道,所有這些都要求用到volatile變量。不懂得volatile的內容將會帶來災難。
假設被面試者正確地回答了這是問題 (嗯,懷疑是否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。
Q 一個參數可以同時是const也是volatile嗎?解釋為什麼。
Q 一個指標可以是volatile 嗎?解釋為什麼。
Q 下面的函數有什麼錯誤︰

int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案︰
A 是的。舉的例子是"只讀的狀態暫存器"。它是volatile因為它可能被意想不到地改變。它是const因為程式不應該試圖去修改它。
A 是的。儘管這並不很常見。舉的例子是當一個執行中的次程序修該一個指向一個buffer 的指標時。
A 這段程式碼有點變態。這段程式碼的目的是用來返指標*ptr指向值的平方,但是,由於*ptr指向一個volatile型參數,編譯器將產生類似下面的程式碼︰
int square(volatile int *ptr)
{
int a, b;
a = *ptr;
b = *ptr;
return a * b;
}
由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段程式碼可能返回不是你所期望的平方值!正確的程式碼如下︰
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}


==============================================


位元操作 (Bit Manipulation)

9.嵌入式系統總是要用戶對變量或暫存器進行位操作。給定一個整型變量a,寫兩段程式碼,第一個設置a的bit 3,第二個清除a 的bit 3。在以上兩個操作中,要保持其它位不變。

對這個問題有三種基本的回應
- 不知道如何下手。該被面者從沒做過任何嵌入式系統的工作。
- 用bit fields。Bit fields是被扔到C語言死角的東西,它保證你的程式碼在不同編譯器之間是不可移植的,同時也保證了的你的程式碼是不可重新使用的。我最近不幸看到Infineon為其較複雜的通信晶片寫的驅動程式,它用到了bit fields因此完全對我無用,因為我的編譯器採用其它的模式來實現bit fields。從道德講︰永遠不要讓一個非嵌入式的家伙沾到實際硬體的邊。
- 用 #defines 和 bit masks 操作。這是一個有極高可移植性的方法, 是應該被用到的方法。
最佳的解決方案如下︰

#define BIT3 (0x1 << 3)
static int a;
void set_bit3(void) {
a = BIT3;
}
void clear_bit3(void) {
a &= ~BIT3;
}
一些人喜歡為設定和清除值而定義一個掩碼同時定義一些說明常數,這也是可以接受的。我希望看到幾個要點︰說明常數、= 和 &=~ 操作。


==============================================


存取固定的記憶體位置 (Accessing fixed memory locations)

10.嵌入式系統經常具有要求程式員去存取某特定的記憶體位置的特點。在某工程中,要求設定一個絕對位址為0x67a9的整數型變數的值為0xaa55。編譯器是一個純粹的ANSI編譯器。寫程式碼去完成這一任務。

這一問題測試你是否知道為了存取一絕對位址把一個整數型強製轉型 (typecast) 為一指標是合法的。這一問題的實作模式隨著個人風格不同而不同。 典型的類似程式碼如下︰

int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
一個較艱澀的方法是︰
*(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二種方案,但我建議你在面試時使用第一種方案。


==============================================


中斷 (Interrupts)

11.中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展-讓標準C支持中斷。具代表的事實是,產生了一個新的關鍵字 __interrupt。下面的程式碼就使用了__interrupt關鍵字去定義了一個中斷服務次程序(ISR),請評論一下這段程式碼的。

__interrupt double compute_area(double radius)
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}

這個函數有太多的錯誤了,以至讓人不知從何說起了︰
- ISR 不能返回一個值。如果你不懂這個,那麼你不會被雇用的。(os : 囧)
- ISR 不能傳遞參數。如果你沒有看到這一點,你被雇用的機會等同第一項。
- 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓多餘的暫存器入棧(PUSH入堆疊),有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。
- 與第三點一脈相承,printf()經常有重入和性能上的問題。 如果你丟掉了第三和第四點,我不會太為難你的。但如果你能得到後兩點,那麼你的被雇用前景越來越光明了。


==============================================


程式碼例子 (Code examples)

12.下面的程式碼輸出是什麼,為什麼?

void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}

這個問題測試你是否懂得C語言中的整數自動轉型原則,我發現極少有開發者懂得這些東西。不管如何,這unsigned int的答案是輸出是 "> 6"。原因是當表達式中存在有符號類型和無符號類型時所有的操作數都自動轉換為無符號類型(unsigned)。因此-20變成了一個非常大的正整數,所以該表達式計算出的結果大于6。這一點對於應當頻繁用到無符號數據類型的嵌入式系統來說是非常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。

13.評價下面的程式碼片斷︰

unsigned int zero = 0;
unsigned int compzero = 0xFFFF; /*1's complement of zero */

對于一個int型不是16位的處理器為說,上面的程式碼是不正確的。應編寫如下︰

unsigned int compzero = ~0;

這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經驗裡,好的嵌入式程式員非常準確地明白硬體的細節和它的限制,然而PC機程式往往把硬體作為一個無法避免的煩惱。
到了這個階段,應試者可能完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應試者不是很好,那麼這個測試就在這裡結束了。但如果顯然應試者做得不錯,那麼我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧…


==============================================


動態記憶體分發 (Dynamic memory allocation)

14.儘管不像非嵌入式計算機那麼常見,嵌入式系統還是有從堆積(heap)中動態分發內存的過程的。那麼嵌入式系統中,動態分發記憶體可能發生的問題是什麼?
這裡,我期望應試者能提到記憶體碎片,碎片收集的問題,變量的生命週期等等。這個主題已經在ESP雜誌中被廣泛地討論過了(主要是 P.J. Plauger,他的解釋遠遠超過我這裡能提到的任何解釋)。我拿出這麼一個小題目給應試者︰
下面的程式碼片段的輸出是什麼,為什麼?

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");

這是一個有趣的問題。最近在我的一個同事不經意把0值傳給了函數malloc,得到了一個合法的指標之後,我才想到這個問題。這就是上面的程式碼,該程式碼的輸出是"Got a valid pointer"。我用這個問題來開始討論,看看被面試者是否想到怎樣做才是正確的。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。


==============================================


Typedef

15.Typedef 在C語言中頻繁用以宣告一個已經存在的資料型態的同義字。也可以用預處理器做類似的事。例如,思考一下下面的例子︰

#define dPS struct s *
typedef struct s * tPS;

以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指標。哪種方法更好呢? (如果有的話)為什麼? 這是一個非常微妙的問題,任何人答對這個問題 (正當的原因) 是應當被恭喜的。
答案是︰typedef更好。思考下面的例子︰
dPS p1, p2;
tPS p3, p4;

第一個擴展為
struct s * p1, p2;

上面的程式碼定義p1為一個指向結構的指標,p2為一個實際的結構,這也許不是你想要的。
第二個例子正確地定義了p3 和p4 兩個指標。


==============================================


艱澀的語法

16.C語言允許一些令人震驚的結構,下面的結構是合法的嗎,如果是,它做些什麼?

int a = 5, b = 7, c;
c = a+++b;

這個問題將做為這個測驗的一個愉快的結尾。不管你相不相信,上面的例子是完全合法的。問題是編譯器如何處理它?水準不高的編譯作者實際上會爭論這個問題,根據最處理原則,編譯器應當能處理儘可能所有合法的用法。因此,上面的程式碼被處理成︰
c = a++ + b;

因此,這段程式碼執行後
a = 6, b = 7, c = 12

如果你知道答案,或猜出正確答案,做得好。如果你不知道答案,我也不把這個當作問題。我發現這個問題的最大好處是這是一個關於程式碼編寫風格,程式碼的可讀性,程式碼的可修改性的好的話題。


==============================================


好了,伙計們,你現下已經做完所有的測試了。這就是我出的C語言測試題,我懷著愉快的心情寫完它,希望你以同樣的心情讀完它。如果是認為這是一個好的測試,那麼盡量都用到你的謀職的過程中去吧。天知道也許過個一兩年,我就不做現下的工作,也需要找一個。
Nigel Jones 是一個顧問,現下住在Maryland。他很高興能收到讀者的來信,他的email位址是: NAJones@compuserve.com


==============================================

[知識]運動完之後餓了請照吃!勿食過量!

Written on 1:28 下午 by Yu Lai

引述自 Personal Fitness Training Theory & Practice

嗯,一般論都是運動後短時間內不應該吃東西,我來提一點不一樣的看法。

其實在運動之後,全身血液的分配會產生暫時性的變化,肌肉充血,而相對的流到脂肪細胞的血就少了。因為血量分配的改變,血中的養分對肌肉與脂肪細胞的分配也會改變,這樣的情況會在半小時內很快的恢復正常。

在有個動物實驗中,運動後的動物被分成兩組,一組在運動後立即給食物,另一組則在四小時候才餵食,十個星期後在體重上雖無顯著差異,但對動物身材卻有顯著影響。立即餵食的動物脂肪組織比延遲餵食的動物脂肪組織重量少24%,且肌肉組織重量增高。該研究的結論是運動後肌肉對養分的競爭力增高,對能源儲存的分配造成明顯的影響。

在人體實驗中也有相似的結果,運動後的肌肉組織對醣類的儲存能力,以及蛋白質的合成能力都暫時增加,但這樣的能力這會在兩小時內明顯減退。另外,肌肉對三酸甘油脂的吸收能力優勢可以維持六個小時。

站在這個立場,運動後的半小時內其實是攝取食物的黃金時段,身體在這時給了一個把脂肪組織打入冷宮的時間,不趁此時更待何時?此外,運動中身體裡的肝醣被大量消耗,因此身體會處在低血醣的狀態,不適時補充一些營養的話,等於為身體準備了一個適合分解肌肉的環境,還維持好幾個小時...你運動是練心酸的嗎?Orz

運動中是消耗,運動完當然要補充。惟補充的應該是一些好吸收消化的東西。另外,絕不可因為『太餓』而過量,這應該很多先賢提過了,所謂運動後馬上吃東西會胖都是因為吃太多...

[技術] 如何避免Linux zombie process的產生

Written on 12:44 下午 by Yu Lai

最近在幫同事debug所遇到的zombie process的問題,Survey了一些心得,就po上來囉。

自Advanced Programming in the Unix Environment這本書:
The process that has terminated, but whose parent hasn't yet waited for it.

簡單的說,所謂zombie process(殭屍行程)的成因,在於某子行程已結束,但其父行程(parent process)並未取得該行程之結束狀態,則該行程就會變成zombie process,直到其父行程呼叫wait()取得該行程結束狀態。

Zombie process會造成kernel內部有多餘的一筆process資料儲存,佔了一筆資料量(process id, termination status, the amount of CPU time taken by the process等等資訊),但其卻已經結束了,若是系統load很高,造成的影響就更大了,譬如process數量到達上限,又發生有zombie process,可能就會產生無法執行程式的問題。因此我們必須避免zombie process的產生。

解決的方法之一就是利用Linux會在一個process的父行程結束時,自動把該行程的父行程變成init,也就是讓該process由init接管。因此,假設我們的父行程必須作自己的事情,並不想wait子行程結束而block時,我們可以利用兩次fork,讓第一次fork的process馬上結束,則第二次fork的grandchild將會自動變成init的子行程,這樣一來,最原先的行程將不必在作wait的動作,而該行程從此也跟第二次fork出來的行程毫無親屬關係了。兩者各作各的,誰先結束並不會影響對方。這就是fork兩次的用意:


if pid = fork()
//parent waits for first child to exit
waitpid(pid);
parent does things here...
else
//child
if fork()
//child
exit;
else
//granchild
grandchild does things here...

[工具] E-Mail圖示產生器

Written on 11:07 上午 by Yu Lai

就是我右邊E-mail那個圖示的產生器囉,
在寫了前三篇後才發現忘了把這個Tool寫個Note啦。

http://services.nexodyne.com/email/

[工具] 筆記本產生器

Written on 11:02 上午 by Yu Lai

PocketMod 網站提供了一項非常有趣且特殊的服務,
您可以在線上設計自己的口袋型記事本,包含封面、
封底一共八頁,僅需使用一張 A4 的紙就可以完成。

http://www.pocketmod.com/app/index.html

[工具] 條紋背景產生器

Written on 11:00 上午 by Yu Lai

Stripe Generator 為一款很有趣的線上工具,
它能夠讓你在線上產生條紋樣式的背景。
可以設定的選項非常多,包含寬度、顏色、樣式、背景顏色、條紋方向等等。

http://www.stripegenerator.com/