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

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++文件進行格式化。