2009年12月12日 星期六

unix shell script來處理時間

#!/bin/bash

now_sec=$(expr `date +%H` \* 60 \* 60 + `date +%M` \* 60 + `date +%S`) 
echo $now_sec 

time_stamp=$(date +"%H %M %S"); 
now_sec=$(expr $(echo $time_stamp | awk '{print $1}') \* 3600) 
now_sec=$(expr $now_sec + $(echo $time_stamp | awk '{print $2}') \* 60) 
now_sec=$(expr $now_sec + $(echo $time_stamp | awk '{print $3}') ) 
echo $now_sec 

#the MSYS doesn't have bc command 
#now_sec=$(echo "$(date +'%H * 3600 + %M * 60 + %S')" | bc ) 
#echo $now_sec 

now_sec=$(($(date +' 10#%H * 3600 + 10#%M * 60 + 10#%S'))) 
echo $now_sec 


這裡列出如何用linux shell script,算出,現在是一天開始的第幾秒,有四個做法,除了第一個做法有疑慮,其它應該都沒問題,不過這樣的演變是有些心路歷程,所以紀錄一下... 

第一個方法,可以看到使用date指令三次,有一種可能錯誤的狀況,就是當時間為1:59:59,第一次date +%H抓到 1, 第二次好死不死,時間變成2:00:00,所以date +%M變成 00,然後date +%S,也許抓到01或02,所以結果可想而知和事實差了1個小時...所以不太好 

第二個方法,就先把時間的字串紀錄下來,然後再透過awk來各別處理...雖然沒問題,但是有點冗長 

第三個方法是我發現date輸出的格式,可以有些調整,所以加了一些輔助的字元,然後轉向給bc這指令算出... 
其實在考慮bc指令前,我有想過expr指令,不過我發現有些困難... 
首先expr指令的算式,各個operator and operand都需要空白隔開...這應該對shell script熟的人,覺得是常識,我還算菜鳥...CC 
再來這個*的運算符號,必須要這樣\*,前面加個escape符號隔開,否則會告訴你說syntax error... 
然後我想出了這個$(expr `date +'%H \* 360 + %M \* 60 + %S'`) 或者這樣$(expr $(date +'%H \* 360 + %M \* 60 + %S')),結果都是syntax error,我搞了半天,還是參不出道理...結果是放棄,換了bc這指令 

其實搞這些,主要是做些實驗,每隔固定時間,做一些存取動作,然後將結果和時間標記存成.csv的格式,而我是在vmware的linux環境下做的,當我開心的寫好script,開始給它一天時間,去收集資料後,才發現到vmware有個bug,就是vmware下的guest os,時間會不準,所以我那個收集資料的script,就破功了...嗚嗚...我google了一下,好像有解決方法,不過我沒去試,因為我想到,如果某個條件後,就可以停止收集資料,那電腦就會一直開著,直到我發現,然後才有機會關掉電腦,這樣有點不環保,浪費電...而在vmware的linux環境下,雖然有指令可以關機,不過關的是vmware的linux,而不是把電腦關機,所以我選擇在windows上執行這個script,當然,windows認不得這個linux shell script,而我也不太想學windows的shell script,所以弄了個MSYS的東東,用它來解譯我之前寫的linux shell script,同時找出在windows下如何透過cmd下命令,來關機,然後加進我寫的shell scirpt,這樣就蠻順心如意了...哈哈 

可是問題又來了,bc這指令沒有也,雖然我可以找到bc的source code透過MinGW編譯出來,或著用cygwin這個比較完善的unix環境(我想它應該有bc),不過我沒選擇前兩條路,我又在shell script這裡下工夫,我又發現,如果這樣寫$((...)),裡面的...會當作算式處理,然後我一開始時是這樣寫的$(($(date +' %H * 3600 + %M * 60 + %S'))),結果跑一段時間就會停掉,檢查一下,發現是因為如果像是01. 02. 012, 其實是被當八進位處理,所以實際十進位是1. 2. 10, 而如果出現08. 09,就會沒法處理,因為八進位只允許0~7,結果再google一下,發現到只要前面加個10#就會告訴shell interpreter,這個數值就會被用十進位來解釋 

看來我希望的目的是達到了...還有一個"date +%s"(小寫),它是給出從1970/1/1到現在的時間秒數,也是可以考慮的時間處理方法 

最後附上windows的關機指令 
關機 shutdown -s 
重開機 shutdown -r 
shutdont -?,查一下,說的很清楚

補充(20091228):
把bash的wildcard(*)關掉,也可以(注意空白)
#!/bin/bash
set -f
now_sec=$(expr $(date +'%H * 360 + %M * 60 + %S'))
echo $now_sec

相關連結:

2009年7月30日 星期四

C 的字串...char* 和 char[]

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

typedef struct {
    char name[20];
    long id;
    long long salary;
} Personal1;

typedef struct {
    char *name;
    long id;
    long long salary;
} Personal2;

Personal1 p1={"Charlie", 1l, 45000l};
Personal2 p2={"Richard", 2l, 45000l};

int main(void)
{
    printf("name:%s id:%ld salary:%ld\n", p1.name, p1.id, p1.salary);
    printf("name:%s id:%ld salary:%ld\n", p2.name, p2.id, p2.salary);

    strcpy(p1.name, "test1");
    //strcpy(p2.name, "test2");  //這行可以compiler過,不過程式執行會當在這

    printf("name:%s id:%ld salary:%ld\n", p1.name, p1.id, p1.salary);
    printf("name:%s id:%ld salary:%ld\n", p2.name, p2.id, p2.salary);

    return 0;
}


首先,這程式有個錯誤的理解,就是在宣告p1.p2同時填入struct裡的內容時,p2的填入過程會填錯...意思是name.id及salary會互相覆蓋之類的...
不過結果不會也,compiler會很聰明的配置一塊區域,給name,然後指向它,其它就正確的填入應該的位置...
接下來又發生一個問題,就是註解掉的那一行,雖然可以編譯通過,不過執行時,會發生Access violation的錯誤....
猜測,這代表編譯器會配置空間放字串,不過是放在唯讀區域

另外又讓我弄了一個實驗,程式如下:
#include <stdio.h>

char test1[]="Charlie";
char *test2="Richard";

int main(void)
{
    printf("%s\n",test1);
    printf("%s\n",test2);
    strcpy(test1,"test1");
    //strcpy(test2,"test2");  //這行可以compiler過,不過程式執行會當在這
    printf("%s\n",test1);
    printf("%s\n",test2);
    return 0;
}

現在我們知道,這兩種預宣告字串內容的方法,對於compiler配置的方法是不同的...

char test1[]="Charlie"; 配置剛好的記憶體空間,並且從literal constant區域複製字串內容過來,而test1只是個常數,代表這個配置空間的第一個位置

char *test2="Richard"; 配置一個指標變數,並且複製literal constant區域內某字串的第一個位置值,所以*test2指向literal constant這個區域,由於唯讀,所以對其修改會產生Access violuation....

另外這些區域的保護作用,也不是編譯器說了算,是要OS(認出來做處裡)和CPU(的機制)互相配合,才可以實現....
若是某些embedded system的情境下,有可能是literal constant的區域(section)是放在唯讀記憶體(或flash),所以無法寫入,或者寫也寫不進,如果經過適當的處理(Relocate 和適當的搬移),把literal constant區域複製到RAM上,則存取就沒問題了

相關連結:
http://twpug.net/docs/ccfaq/node16.html#q:1.32
http://www.yuanma.org/data/2007/0305/article_2375.htm
http://birdegg.wordpress.com/2006/10/21/re-問題-cc中char與char的差異/

2009年5月15日 星期五

Wake on LAN 軟體和介紹

Wake on LAN,就是透過Ethernet來啟動主機,主機有些需求及設定(參考相關文章),
軟體方面就是送個magic packet...
可以在LAN運作,也可以跨網來運作,不過跨網運作,需要Router支援

基本上這個magic packet是個UDP封包,不過它的DATAGRAM,包括了一串6個位元組的0xFF同步串流(Sync Stream),然後是重複16次要喚醒主機的MAC位址,所以DATAGRAM長度是102個位元組

建議是用UDP port 7,不過用Wireshark來監看時,它會以為是ECHO協定的request...
PS : 在公司測試時,發現有一台機器會回我ECHO協定的Response(我又不方便問這台機器是做啥的),不過也許是這個原因,讓Solarwind的Wake-On-LAN軟體(只能用port 7),沒法成功喚醒我希望喚醒的主機


例如AMD的magic_pkt.exe是用Port 2304,Wireshark可以辨識出來,
基本上,如果不是用了一些有名號的協定所用的port,Wireshark都可以分辨出來
或者,用Port 9,也是建議選項(也許我應該找協定規格看看....)

關於Router的支援:

就我的認知,Router(或IP分享器)的支援,需要開放一個Port(7或9),所有進來的封包,都要修改IP目的位址為255.255.255.255,這樣封包的目的MAC位址也會變成FF-FF-FF-FF-FF-FF,然後送到LAN廣播去

我們不需要將目的MAC位址換成要喚醒主機的MAC位址,被喚醒的主機是檢查UDP的DATAGRAM來確定是否要喚醒,當然,也許置換成喚醒主機的MAC位址,也可以,不過沒試過...

這樣的設定,有很大的風險,只要透過這個Port,就可以對LAN內的機器做廣播,要是某一台機器有一些漏洞,那就中招了,所以應該多些規則來限制,例如只能是UDP封包,還有能辨識出Magic Packet的封包,取出要喚醒機器的MAC位址,來和設定允許主機的MAC位址列表來比對,不過這樣Router的Loading就大了(或者稱是特殊型的NAT)

PS 1:出個題目,用Linux主機來設定這樣的功能,有機會試試看!!!
PS 2:我不知道有哪些IP分享器有支援,事實上,跨網的運作我也沒試過...
PS 3:下面連結,介紹了三款軟體,在LAN內測試時,
        若有需要IP位址,就輸入255.255.255.255,若有需要NETMASK,也輸入255.255.255.255

注意事項:
1. 有些舊機型,雖然有支援,不過如果有斷電的情形(像是插頭拔掉再插回去),會無法initial,監控magic packet
2. 如果機器是按著power鍵來關掉的,即使有支援,機器也會無法監控magic packet

Qt Creator練習:這個題目很簡單,拿來練習工具的使用,蠻好的,之後也許寫篇"Qt Creator的使用心得"
下載

用C++ Builder寫Wake on LAN的功能:
http://hugolin888.blogspot.com/2007/02/wake-on-lan.html

透過網站服務來傳送magic packet...蠻方便的,不過還沒試過
http://www.dslreports.com/wakeup
http://www.wakeonlan.me/

軟體:
http://www.amd.com/us-en/assets/content_type/utilities/magic_pkt.exe
http://www.solarwinds.com/products/freetools/wake_on_lan.aspx
http://www.depicus.com/wake-on-lan/wake-on-lan-gui.aspx

參考連結:
http://www.wretch.cc/blog/josephphoto/3511150
http://www.wells.hk/ws_toolsdetail.php?tools_id=1103103253
http://myhome.ethome.com.tw/xanche/p200708300.htm
http://www.smallnetbuilder.com/content/view/29941/53/
http://wordpress.morezman.com/?p=100
維基百科


2009年4月28日 星期二

Qt4.5.1 & 靜態編譯

一直想要試試看Qt靜態編譯的方法,看了很多文章,總覺得很麻煩,總希望那一天Qt(現在是Nokia的公司)出新的版本,
只要 qmake -static -project -o yes.pro 就搞定, CC...

不過我幻想的情況沒來,卻來了一個LGPL授權方式...
這個東西,簡單講,就是Qt所提供的GUI library,如果以動態連接的方式使用,則原程式設計者,可以擁有發佈形式的權利...
所以,對於商業應用的人來說,可以不需權利金,就能發展商業應用,看來是個雙贏的策略

因此,靜態編譯的方法,似乎吸引力就沒這麼大了...
但是既然看了很多文章,就這樣放水流,也可惜,還是記錄一下,順便把我那一堆書籤消化一下

目前Qt的4.5版本,已經採用了LGPL授權方式,最新版本4.5.1
下載 的Qt SDK包括了Qt. MinGW和Qt Creator

隨著Qt SDK下載回來的MinGW,在命令模式輸入gcc --version顯示3.4.2(mingw-special)

實驗記錄:
1. 下載qt-win-opensource-2009.02.exe ,安裝
2. 複製Qt下的"2009.02"目錄,取名叫"4.5.1-static"
3. 執行Qt Command Prompt,切換到"4.5.1-static\qt"目錄下
4. 輸入configure -static -release  -no-exceptions ,執行(大概25分鐘)
5. 輸入mingw32-make sub-src,執行(大約2個多小時)
6. 輸入mingw32-make clean,之後整個"4.5.1-static"目錄大約1GB
7. 砍掉"4.5.1-static\qt"目錄下所有.dll檔,之後整個目錄大約672MB
8. 請將這目錄"4.5.1-static"好好保存起來,不然再做一次,很浪費電...

若需要編譯成靜態連結的方式,就將目錄裡qt\bin設進PATH裡,就可以用了

目前編譯環境MinGW/MSYS...對於Qt Creator這個IDE,還不熟,我想應該不錯用...
至於Qt Creator的靜態連結設定,有機會再研究...

PS 1: 編譯完的library,只能編譯release版本(代表沒有debug資訊),且是靜態連接,符合一般發佈的情況,不過License必須使用GPL
PS 2: -no-exceptions是要拿掉mingwm10.dll相依,不過沒有exception會有啥影響,未知...
PS 3: 一些plugin型式的library,需要特別處理,請參考介紹的文章
PS 4: 在XP sp3沒法編譯完成,還有從相關文章報告,windows 7也有些問題,我是在XP sp2環境下完成

相關連結:

這裡有一位作者講解LGPL授權
http://jay-notebook.blogspot.com/2009/01/qt-45-lgpl-21.html

其他作者的文章,關於靜態編譯的方法
http://hi.baidu.com/jzinfo/blog/item/c17c3f6d4ff439fe431694c3.html
http://www.wretch.cc/blog/jaywang4/15097262
http://lists.trolltech.com/qt-interest/2006-08/thread00942-0.html
http://lists.trolltech.com/qt-interest/2005-10/thread00381-0.html#msg00553
http://www.linuxfans.org/bbs/thread-182853-1-1.html
http://www.dotblogs.com.tw/yotrew/archive/2009/03/19/7568.aspx
http://www.qtforum.org/article/26946/how-to-deploy-a-static-app-in-qt-4-5.html
http://blog.yam.com/kaizan/article/20066326
http://jarsing.blogspot.com/2009/03/qtqtdll.html

2009年4月24日 星期五

MinGW和VC2008E之間...DLL編譯

測試程式,和相關操作,請參考之前兩篇文章(參考1 ,參考2 )

1. VC2008E使用MinGW產生的DLL檔
MinGW編譯DLL,可以產生testdll.def檔,然後利用VC的工具轉成VC相容的testdll.lib,如下:
lib /machine:i386 /def:testdll.def
之後產生的testdll.lib,隨同testdll.h,testdll.dll就可以和VC的專案做編譯連結了

PS1:MinGW產生的.lib,格式和VC不相容
PS2:可以使用VC提供的"Visual Studio 2008 命令提示字元",來做.def to .lib的轉換,它提供一個乾淨的VC建置環境,不過DOS的指令要有些印象...
PS3:透過.def轉換到.lib,然後連結後,產生的執行檔,就會找和.def同主檔名的.dll

2. MinGW使用VC2008E產生的DLL檔
VC編譯的DLL,基本上MinGW就可以用,不過要注意,在VC的設定裡,"呼叫慣例"(calling convention),要設成__cdecl,
若是__stdcall,就需要其他處理...
在VC2008的屬性頁視窗,組態屬性->C/C++->進階,呼叫慣例,改成__cdecl

編譯好後,將testdll.lib, testdll.h, testdll.dll,放到MinGW的專案目錄,就可以編譯連結了

後記:
這篇基本上就是參考MinGW的說明,只是筆記

相關連結:
http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs
維尼的蜂巢:__stdcall ,__cdecl的區別
維尼的蜂巢:__stdcall ,__cdecl的區別 再次探討
http://msdn.microsoft.com/zh-tw/library/46t77ak2.aspx

2009年4月21日 星期二

VC2008 Express 編譯DLL

建立DLL專案:
檔案->新增->專案

選擇Win32,Win32專案

按"下一步"

選擇DLL,然後按完成

修改testdll.cpp,以及加入testdll.h在標頭檔分類裡,而其內容,如之前的文章
PS: testdll.h的加入,可以按右鍵在標頭檔,然後選擇加入->新增項目,選"標頭檔(.h)",名稱輸入testdll.h
可以在方案總管,看到VC幫我們加進來的檔案

修改屬性,在testdll的c++ project上按右鍵(就是標頭檔的上一個階層,或方案的下一個階層),選擇屬性
組態屬性->C/C++->前置處理器,在前置處理器定義裡加進DLL_EXPORT(用分號相隔),確定

選擇建置->建置方案,就可以建置DLL了,可以在方案下的Debug目錄看到testdll.dll和testdll.lib,以及在testdll的目錄裡看到testdll.h,這三個檔案是我們需要的


建置win32 主控台應用程式:檔案->新增->專案

選擇Win32,Win32主控台應用專案

按"完成"

可以在方案總管,看到vc幫我們加進來的檔案
修改testmain.cpp,如之前的文章 ,關於_tmain(...),覺得很詭異的話,之前有些文章有介紹


有三個檔案要加進來,首先
testdll.lib和testdll.h放到tsetmain目錄裡,(testdll.h可以加入標頭檔,方便修改,不過沒差)
lib的加入,進入屬性頁面,組態屬性->連結器->輸入,其他相依性,輸入testdll.lib,確定

選擇建置->建置方案,就可以建置了,不過執行會找不到dll...
將testdll.dll放到執行檔產生的目錄(例如Debug\),就ok了

問題:如果硬碟是FAT32的格式,會有一些問題,有其他高手有紀錄下來,及解決方式
這裡做個備忘
Step1 於系統功能表中點擊「專案」→「屬性」(或按Alt + F7)
Step2 展開「組態屬性」
Step3 展開「連結器」
Step4 點擊「偵錯」
Step5 把右方視窗裡的「產生對應檔」改為「是(/MAP)」
Step6 點擊「資訊清單工具」
Step7 把右方視窗裡的「使用 FAT32 解決辦法」改為「是」

後記:Visual Studio 2008 Express(包括了VC),她有分方案和專案,一個方案可以包括一個以上的專案,這些專案可以用VC或VB來寫,或是一個DLL的專案,上面的例子,是兩個方案(各擁有一個專案),透過手動的方式來組合,如果一個方案包括兩個專案,一個是testdll,一個是testmain,則這個IDE會幫你處理一些事,但使用者還是要有些設定...
Step1  在方案的階層,將這兩個專案建立或加入
Step2  testdll.h需要放在各自source(.cpp .h放的地方)的目錄,然後testdll專案需要定義DLL_EXPORT
Step3  testmain專案"設定為啟始專案","專案相依性"設定為testmain相依testdll
PS : 上面提到lib的加入(進入屬性頁面,組態屬性->連結器->輸入,其他相依性,輸入testdll.lib,確定),不需要...

寫這篇真的是服務的心態(對於初學者),也許也希望對於DLL的編譯有個完整性的介紹,不過抓這麼多圖,是有點無聊,佔頁面,不過沒辦法,不然就乾脆不寫了

最近"Google 文件"對於Blogger的輸出,一些bug,有改善了(像是之前圖檔會亂跑,增加許多空白行...),所以這篇文章也算是對"Google 文件"的測試,看來應該是可以重回"Google 文件"的懷抱了

相關連結:
http://genewince.blogspot.com/2008/03/visual-c-dll-gene.html
http://www.wretch.cc/blog/MtvBoy/21514680
http://logix4u.net/Programming/vc++/A_Tutorial_on_creating_DLLs_with_VC++.html

2009年4月19日 星期日

用MinGW編譯DLL

testdll.h:
#ifndef TESTDLL_H
#define TESTDLL_H

#ifdef DLL_EXPORT
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C" {
#endif

DLLAPI float funadd(float a, float b);

#ifdef __cplusplus
}
#endif

#endif


testmain.c:
#include <stdio.h>
#include "testdll.h"

int main(void)
{
float a,b,c;

    a=1.0;
    b=2.0;
    c=funadd(a,b);
    
    printf("%f \n",c);
    getchar();    
}


testdll.c:
#include "testdll.h"

float funadd(float a, float b)
{
    return (a+b);
}


gcc -c -DDLL_EXPORT (-fPIC) testdll.c
PS : 會產生testdll.o的檔案...還有x86平台,-fPIC會忽略

gcc -shared -o testdll.dll testdll.o -Wl,--output-def,testdll.def,--out-implib,testdll.lib
PS : 產生三個檔案:
testdll.dll:也就是程式執行時載入的dll檔
testdll.def:若有需要和VC編譯器連結,這個檔會需要
testdll.lib:export library,用來連結主程式,這樣主程式才能知道找哪個dll,那個dll裡有哪些函式跟參數....

gcc -c testmain.c
PS : 用來使用dll的主程式,會產生一個testmain.o的檔案

gcc -static -o test.exe testmain.o -L. -ltestdll
PS : 會讓主程式和export library(testdll.lib)靜態連結起來,產生test.exe的可執行檔


由於dll和主程式都是用MinGW產生的,所以可以簡化上面的步驟,不過上面的解說,可以對執行檔組成了解多一些,以下舉兩個例子

方法一:
gcc -shared -o testdll.dll testdll.c
PS : -shared是linker的參數,這裡直接產生testdll.dll

gcc -c testmain.c

PS : -c:只編譯不連結,產生testmain.o

gcc -o test.exe testmain.o testdll.dll

PS : 我也不知給什麼規則,反正MinGW搞得定這個testdll.dll就對了

方法二:
gcc -shared -o testdll.dll testdll.c
gcc -o test.exe testmain.c -L. -ltestdll

PS : -l:unix環境裡,會找libtestdll.a(-static) 或 libtestdll.so(-shared),在函式庫名稱的命名上,前面一定要有lib

不過MinGW對於這個慣例,放寬許多,來讓熟悉windows的人習慣
for (-static) : 會找libtestdll.a 或 testdll.lib
for (-shared) : 會找libtestdll.so . testdll.dll 或 libtestdll.dll

希望看完之後是感覺"條條道路通羅馬",而不是給它一整個亂...

這裡又牽扯出另一個疑問,那就是MinGW的make,它對於字尾規則和函式庫的處理,是否有加上這些擴充,有空在研究好了

PS:MinGW的官方網站,有提供一個Shell,叫MSYS,之前發現MinGW和MSYS都有自己的make,MinGW的make叫mingw32-make.exe,MSYS的make叫make.exe,其中MSYS的make是unix style,不能分辨'\',組成的路徑字串(windows是用'\',組成路徑字串),也許這兩個make除了這個區別,還包括了字尾規則和函式庫處理的差異...


testdll.h的補充說明:
關於DLL_EXPORT 的定義,應該要在編譯testdll.c時定義,來分別dllimport和dllexport,不過我沒加,也沒錯誤,我不知道MinGW有沒有支援__declspec(dllimport)和__declspec(dllexport) 這些關鍵字...

關於extern "C" {},如果使用gcc,且souce code的附檔名是.c,會有錯誤,不過__cplusplus沒有定義,會避開...
如果附檔名是.cpp,或gcc改g++,則__cplusplus會定義,就會使函數的Name Mangling使用C的形式(簡單的那一種),常看到...

這裡的紀錄,主要都是看了別人的文章,試出來的...我盡量有系統性,不過最好還是參考相關主要說明文件

相關連結:

MinGW官方整理的FAQ
http://www.mingw.org/wiki/FAQ

這裡有幾篇關於如何用MinGW編譯DLL
http://www.adp-gmbh.ch/win/misc/mingw/dll.html
http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs
http://sig9.com/node/35

用cygwin編譯DLL
http://mqjing.blogspot.com/2009/03/c-cygwin-windows-code.html
http://mqjing.blogspot.com/2009/04/c-gcc-library.html 

用gcc處理static and shared library
http://www.adp-gmbh.ch/cpp/gcc/create_lib.html

有關gcc和g++的區別
http://blog.chinaunix.net/u1/52901/showart_480159.html
http://www.yuanma.org/data/2007/0406/article_2498.htm

2009年2月26日 星期四

ARM的例外事件處理和管線的關係

當例外發生時,處理器會負責如下的動作
1. 拷貝CPSR到對應例外處理模式的SPSR
2. 改變CPSR到對應的例外處理模式,以及對應處理模式的暫存器群,
    任何例外處理都會disable IRQ位元,在FIQ和RESET的例外發生時,才會disable FIQ位元
3. 拷貝PC(r15)到對應處理模式的LR(r14)
4. 設定PC(r15)到對應中斷向量的位址



SWI和Undefined instruction的例外事件:
這兩種例外,都是執行指令所引起的,且都是在PC(r15)沒有更新前,去處理例外事件,所以回到原來的執行程序時,需要執行下一個的指令,因為前一個指令,就是呼叫例外處理程式處理
(for undefined instruction:這個例外事件程式可以當作發展自有命令的模擬開發方式)
the return instruction is:
MOVS pc, lr
The handler entry and exit code to stack the return address and pop it on return is:
STMFD sp!,{reglist,lr}
;...
LDMFD sp!,{reglist,pc}^


prefetch abort的例外事件:
若fetch的指令為無效的位址,在fetch的階段就會標記這個指令,不過要等到Execute階段才會去處理例外事件,這時這個例外事件可能透過MMU對應vitrual memory locations到正確的physical memory,也許還有physical memory和儲存設備之間的swap動作...
PS:在Execute階段處理例外事件時,pc(r15)還未更新
然後回到原來程序時,應該跳回到原來的指令,這時就可以正確fetch這個位址的指令,所以
the return instruction is:
SUBS pc,lr, #4
The handler entry and exit code to stack the return address and pop it on return is:
SUB lr,lr,#4
STMFD sp!,{reglist,lr}
;...
LDMFD sp!,{reglist,pc}^



FIQ和IRQ的例外事件:
在執行完指令完且更新PC(r15)後,會檢查IRQ及FIQ的腳位是否為Low,若成立則處理例外事件,所以回到原來的執行程序時,需要執行下一個的指令,因為前一個指令,已經執行過了
the return instruction is:
SUBS  pc, lr, #4
The handler entry and exit code to stack the return address and pop it on return is:
SUB lr,lr,#4
STMFD sp!,{reglist,lr}
;...
LDMFD sp!,{reglist,pc}^

data abort的例外事件:
在執行load或store的相關指令時,執行完且更新PC(r15)後,才會發生data abort的例外事件,這時這個例外事件可能透過MMU對應vitrual memory locations到正確的physical memory,也許還有physical memory和儲存設備之間的swap動作...
所以回到原來程序時,應該跳回到原來的指令,然後就可以正確load或stroe這個位址的資料,所以
the return instruction is:
SUBS pc, lr, #8
The handler entry and exit code to stack the return address and pop it on return is:
SUB lr,lr,#8
STMFD sp!,{reglist,lr}
;...
LDMFD sp!,{reglist,pc}^

    似乎這些例外處理的返回位址計算,應該是CPU的責任,不過當年ARM CPU的其中一個主要訴求,就是很小的晶片面積,所以就不浪費邏輯閘,讓軟體來處理這些動作,或者說讓軟體開發者有更大的彈性...
    目前有接觸過ARM 7,擁有三階的管線(Fetch. Decode. Execute),之後更強大的ARM系列的CPU,有提供5階或更多的管線,不知道例外處理返回位址的計算,是不是不同???(有機會碰到在看看)
    雖然這兩張圖用來代表管線處理指令流的示意,也許有不恰當的地方,不過我喜歡這樣的描述方式來記憶返回位址的計算,重點是例外發生時,PC(r15)是否有更新,以及各種例外對於返回後,處理的需求,就可以推論返回位址的計算

相關連結:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0040d/index.html 第九章相關內容
http://infocenter.arm.com/help/topic/com.arm.doc.dui0040d/DUI0040D.pdf ,pdf文件下載