2007年6月5日 星期二

GNUARM命令行工具基本使用入門

GNUARM命令行工具基本使用入門
調用格式:
arm-elf-gcc [stage-opt] [other-opts] -mcpu=arm7tdmi in-file -o out-file
常見用法:
將C代碼編譯為二進制目標文件:
arm-elf-gcc -c -O2 -g -mcpu=arm7tdmi filename.c -o filename.o
將多個二進制目標文件合并為一個可執行文件:
arm-elf-ld filename1.o filename2.o … -o filename.elf
將C代碼直接編譯生成可執行文件:
arm-elf-gcc -O2 -g -mcpu=arm7tdmi filename.c -o filename.elf
將C代碼編譯生成匯編代碼:
arm-elf-gcc -S -fverbose-asm -mcpu=arm7tdmi filename.c -o filename.s
arm-elf-objdump option filename | more
例如:arm-elf-objdump -S a2.o
使用readelf查看elf文件的内容,例如:arm-elf-readelf -a a2.elf
arm-elf-objcopy有一個很重要的作用是把代碼從elf文件中抽取出來,形成可執行的機器碼:
例如:arm-elf-objcopy -O binary -R .comment -R .note -S a2.elf a2.bin
形成的結果文件a2.bin可以焼到flash或下載到内存中去.
arm-elf-nm用來列出elf文件中使用到的symbol,例如:arm-elf-nm a1.o

http://www.micetek.com.cn/technic/gcc.pdf

2007年5月31日 星期四

伊凡塞斯 Evanescence-Bring Me To Life(mv)



http://blog.xuite.net/franky7120/music/7614169
伊凡塞斯(樂團)-讓我重生
夜魔俠/Dare Devil電影主題曲
主唱 :Amy Lee

How can you see into my eyes like open doors
Leading you down into my core where I've become so numb
Without a soul, my spirit sleeping somewhere cold
Until you find it there and lead it back home

(Wake me up) Wake me up inside
(I can't wake up) Wake me up inside
(Save me) Call my name and save me from the dark
(Wake me up) Bid my blood to run
(I can't wake up) Before I come undone
(Save me) Save me from the nothing I've become

Now that I know what I'm without
You can't just leave me
Breathe into me and make me real
Bring me to life

(Wake me up) Wake me up inside
(I can't wake up) Wake me up inside
(Save me) Call my name and save me from the dark
(Wake me up) Bid my blood to run
(I can't wake up) Before I come undone
(Save me) Save me from the nothing I've become

Bring me to life
(I've been living a lie, there's nothing inside)
Bring me to life

Frozen inside without your touch, without your love, drling
Only you are the life among the dead

(All this time I can't believe I couldn't see)
(Kept in the dark but you were there in front of me)
I've been sleeping a thousand years it seems
Got to open my eyes to everything

(Without a thought, without a voice, without a soul)
(Don't let me die here)
(There must be something wrong!)
Bring me to life

(Wake me up) Wake me up inside
(I can't wake up) Wake me up inside
(Save me) Call my name and save me from the dark
(Wake me up) Bid my blood to run
(I can't wake up) Before I come undone
(Save me) Save me from the nothing I've become

Bring me to life
(I've been living a lie, there's nothing inside)
Bring me to life

你是如何像開門般地看穿了我的眼睛
引導降臨至我那已變得麻木不仁的內心深處
失去了靈魂,我的精神沉睡在寒冷的某處
直到被你尋獲並引領它歸來

[喚醒我] 自內心深處喚醒我
[我無法醒來] 自內心深處喚醒我
[救救我] 呼喚我,釋放那陷在黑暗中的我
[喚醒我] 讓我的血液再次汌流
[我無法醒來] 在我崩潰之前
[救救我] 拯救那即將幻滅的我

現在我知道我缺少了什麼
你不能就這樣丟下我
請賦予我生氣讓我回歸真實
請賜予我生命

[喚醒我] 自內心深處喚醒我
[我無法醒來] 自內心深處喚醒我
[救救我] 呼喚我,釋放那陷在黑暗中的我
[喚醒我] 讓我的血液再次汌流
[我無法醒來] 在我崩潰之前
[救救我] 拯救那即將幻滅的我

請賜予我生命
[我如謊言般虛假地活著,內心是何等的空乏]
請賜予我生命

我心冰封,除非你的輕撫、你的關愛,親愛的
這一片死寂之中唯有你才是存在

[我不敢相信,泥陷在黑暗的這些期間裡
你已經在我的面前而我竟未看見]
我彷彿已經沉睡了千年之久
現在才得以張開眼睛看到萬物

[沒有思考,沒有聲響,沒有靈魂]
[別讓我死在這裡]
[這一定有什麼不對勁]
請賜予我生命

[喚醒我] 自內心深處喚醒我
[我無法醒來] 自內心深處喚醒我
[救救我] 呼喚我,釋放那陷在黑暗中的我
[喚醒我] 讓我的血液再次汌流
[我無法醒來] 在我崩潰之前
[救救我] 拯救那即將幻滅的我

請賜予我生命
[我如謊言般虛假地活著,內心是何等的空乏]
請賜予我生命 (Translate:大楞子)

[轉]likely,unlikely巨集與GCC內建函數__builtin_expect()

來源:http://blog.csdn.net/mopyman/archive/2006/02/09/595302.aspx

最近在讀linux 2.6 内核,雖然以前已經看了很多相關的知識,<>也看了2,3遍,但讀2.6内核仍然感到很吃力。面對2.6如此龐大的内核,信心真的不是很足,而且好像也没有很好的、有幫助的論壇來一起探討,哎!現在正在邊看<<情景分析>>,邊看最新的内核,自<<情景分析>>出版以來,内核已經有了很多的變化,好難讀啊!如果這様讀下去算不算“皓首窮經”呢,不得而知了!
言歸正傳
在讀linux/kernel/fork.c的時候遇到了unlikely宏定義,一路追踪,最後找到了GCC内建函數__builtin_expect(),查閲GCC手册,發現其定義如下:
long __builtin_expect (long exp, long c) [Built-in Function]
You may use __builtin_expect to provide the compiler with branch prediction
information. In general, you should prefer to use actual profile feedback for this
(‘-fprofile-arcs’), as programmers are notoriously bad at predicting how their
programs actually perform. However, there are applications in which this data is
hard to collect.
The return value is the value of exp, which should be an integral expression. The
value of c must be a compile-time constant. The semantics of the built-in are that it
is expected that exp == c. For example:
if (__builtin_expect (x, 0))
foo ();
would indicate that we do not expect to call foo, since we expect x to be zero. Since
you are limited to integral expressions for exp, you should use constructions such as
if (__builtin_expect (ptr != NULL, 1))
error ();
when testing pointer or floating-point values.
大致是説,由于大部分程序員在分支預測方面做得很糟糕,所以GCC提供了這個内建函數來幫助程序員處理分支預測,優化程序。其第一個參數exp為一個整型表達式,這個内建函數的返回值也是這個exp,而c為一個編譯期常量,這個函數的語義是:你期望exp表達式的值等于常量c,從而GCC為你優化程序,將符合這個條件的分支放在合適的地方。
因為這個程序只提供了整型表達式,所以如果你要優化其他類型的表達式,可以采用指針的形式。

unlikely的定義如下:
#define unlikely(x) __builtin_expect(!!(x), 0)
也就是説我們期望表達式x的值為0,從而如果我們用
…….
if(unlikely(x)){
bar();
}
來測試條件的話,我們就不期望bar()函數執行,所以該宏的名字用unlikely也就是不太可能來表示。
likely宏與次類似.

説到底__builtin_expect函數就是為了優化可能性大的分支程序。

2007年5月29日 星期二

我家女兒學會倒退爬囉!

曾經有一天晚上半夜時被我女兒嚇一跳,因為她突然放聲大哭...
而我被吵醒時卻沒發現她的身影...東翻西翻才發現她居然自己鑽到床中央,
躲在綿被裏....可能是太熱了才大哭吧.

後來褓姆也證實了....寶寶學會了新技能...會爾偶一點點一點點的後退爬...哈

超級星光大道-蕭敬騰-世界唯一的你

超級星光大道-梁文音 失戀無罪

2007年4月26日 星期四

C#面試題

http://www.fandl.cn/article.asp?id=49
C#面試題

一.填空題
1.c#中的三元運算符是_____?
2.當整數a賦值給一個object物件時,整數a將會被_____?
3.類成員有_____種可訪問形式?
4.public static const int A=1;這段代碼有錯誤麼?是什麼?
5.float f=-123.567F;
int i=(int)f;
i的值現在是_____?
6.利用operator聲明且僅聲明了==,有什麼錯誤麼?
7.委託聲明的關鍵字是______?
8.用sealed修飾的類有什麼特點?
9.在Asp.net中所有的自定義用戶控制項都必須繼承自________?
10.在.Net中所有可序列化的類都被標記為_____?
11.在.Net託管代碼中我們不用擔心記憶體漏洞,這是因為有了______?
12.下面的代碼中有什麼錯誤嗎?_______
using System;
class A
{
public virtual void F(){
Console.WriteLine("A.F");
}
}
abstract class B:A
{
public abstract override void F();
}

13.當類T只聲明了私有實例構造函數時,則在T的程式文本外部,______(可以 or 不可以)從T

派生出新的類,____(可以 or 不可以)直接創建T的任何實例。
14.下面這段代碼有錯誤麼?
switch (i){
case():
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
dufault;
CaseTwo();
break;
}
15.在.Net中,類System.Web.UI.Page 可以被繼承麼?

二.簡答題
1.在c#中using和new這兩個關鍵字有什麼意義,請寫出你所知道的意義?
2.在下面的例子裏
using System;
class A
{
public A(){
PrintFields();
}
public virtual void PrintFields(){}
}
class B:A
{
int x=1;
int y;
public B(){
y=-1;
}
public override void PrintFields(){
Console.WriteLine("x={0},y={1}",x,y);
}
當使用new B()創建B的實例時,產生什麼輸出?
3.下面的例子中

using System;
class A
{
public static int X;
static A(){
X=B.Y+1;
}
}
class B
{
public static int Y=A.X+1;
static B(){}
static void Main(){
Console.WriteLine("X={0},Y={1}",A.X,B.Y);
}
}
產生的輸出結果是什麼?
4.談談類和結構的區別?
5.一個長度為10000的字串,通過隨機從a-z中抽取10000個字元組成。請用c#語言編寫主要程

序來實現。
6.對於這樣的一個枚舉類型:
enum Color:byte{
Red,
Green,
Blue,
Orange
}
試寫一段程式顯示出枚舉類型中定義的所有符號名稱以及它們對應的數值。
7.您瞭解設計模式麼?請列出您所知道的設計模式的名稱。
8.請在SQL Server中設計表來保存一個樹狀結構的組織結構圖(假設結構圖中只有名稱這一項內容

需要保存),如果我想查詢某一職位下的所有職位,用一個存儲過程來實現,你有什麼思路?
9.什麼叫做SQL注入,如何防止?請舉例說明。
10.下面這段代碼輸出什麼?為什麼?
int i=5;
int j=5;
if (Object.ReferenceEquals(i,j))
Console.WriteLine("Equal");
else
Console.WriteLine("Not Equal");

1 ?:
2 裝箱
3 3種
4 const成員都是static所以應該去掉static
5 -123
6 要同時修改Equale和GetHash() ? 重載了"==" 就必須重載 "!="
7 delegate
8 不可被繼承
9 System.Web.UI.UserControl
10 [serializable]
11 gC
12 abstract override 是不可以一起修飾
13 不可以,不可以
14 case():不行 default;
15 可以

1 Using 引入一個名子空間,或在使用了一個對像後自動調用其IDespose,New 實例化一個對

像,或修飾一個方法,表此方法完全重寫此方法,
2 X=1,Y=0
3 x=1,y=2
4 最大區別一個是引用類型,一個是值類型 默認成員訪問為public是另外一個區別

.NET & C# 基礎知識試題 (20%)
1. 在.net(C# or vb.net)中如何獲得當前表單或控制項的控制碼,特別是控制項本身的控制碼(請列舉)。
答案:this(C#) Me(vb.net).

2. 在.net(C# or vb.net)中如何用戶自定義消息,並在表單中處理這些消息。
答案:
在form中重載DefWndProc函數來處理消息:
protected override void DefWndProc ( ref System.WinForms.Message m )
{
switch(m.msg)
{
case WM_Lbutton :
 ///string與MFC中的CString的Format函數的使用方法有所不同
 string message = string.Format("收到消息!參數為:{0},{1}",m.wParam,m.lParam);
 MessageBox.Show(message);///顯示一個訊息方塊
 break;
case USER:
處理的代碼
default:
 base.DefWndProc(ref m);///調用基類函數處理非自定義消息。
 break;
}
}

3. 在.net(C# or vb.net)如何啟動另一個程式。
答案:process

4. 在.net(C# or vb.net)中如何取消一個表單的關閉。
答案:
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel=true;
}

5. 在.net(C# or vb.net)中,Appplication.Exit 還是 Form.Close有什麼不同?
答案:一個是退出整個應用程式,一個是關閉其中一個form

6. 在C#中有一個double型的變數,比如10321.5,比如122235401.21644,作為貨幣的值如何按各個不同國家的習慣來輸出。比如美國用$10,321.50和$122,235,401.22而在英國則為£10 321.50和£122 235 401.22
答案:
System.Globalization.CultureInfo MyCulture = new System.Globalization.CultureInfo("en-US");
//System.Globalization.CultureInfo MyCulture = new System.Globalization.CultureInfo("en-GB");為英國貨幣類型
decimal y = 9999999999999999999999999999m;
string str = String.Format(MyCulture,"My amount = {0:c}",y);

7. 某一密碼僅使用K、L、M、N、O共5個字母,密碼中的單詞從左向右排列,密碼單詞必須遵循如下規則:
(1) 密碼單詞的最小長度是兩個字母,可以相同,也可以不同
(2) K不可能是單詞的第一個字母
(3) 如果L出現,則出現次數不止一次
(4) M不能使最後一個也不能是倒數第二個字母
(5) K出現,則N就一定出現
(6) O如果是最後一個字母,則L一定出現
問題一:下列哪一個字母可以放在LO中的O後面,形成一個3個字母的密碼單詞?
A) K B)L C) M D) N
答案:B

問題二:如果能得到的字母是K、L、M,那麼能夠形成的兩個字母長的密碼單詞的總數是多少?
A)1個 B)3個 C)6個 D)9個
答案:A

問題三:下列哪一個是單詞密碼?
A) KLLN B) LOML C) MLLO D)NMKO
答案:C

8. 62-63=1 等式不成立,請移動一個數字(不可以移動減號和等於號),使得等式成立,如何移動?
答案:62移動成2的6次方

C/C++語言struct深層探索

C/C++語言struct深層探索
1. struct的巨大作用
  面對一個人的大型C/C++程式時,只看其對struct的使用情況我們就可以對其編寫者的編程經驗進行評估。因為一個大型的C/C++程式,勢必要涉及一些(甚至大量)進行資料組合的結構體,這些結構體可以將原本意義屬於一個整體的資料組合在一起。從某種程度上來說,會不會用struct,怎樣用struct是區別一個開發人員是否具備豐富開發經歷的標誌。

  在網路協定、通信控制、嵌入式系統的C/C++編程中,我們經常要傳送的不是簡單的位元組流(char型陣列),而是多種資料組合起來的一個整體,其表現形式是一個結構體。

  經驗不足的開發人員往往將所有需要傳送的內容依順序保存在char型陣列中,通過指標偏移的方法傳送網路報文等資訊。這樣做編程複雜,易出錯,而且一旦控制方式及通信協定有所變化,程式就要進行非常細緻的修改。

  一個有經驗的開發者則靈活運用結構體,舉一個例子,假設網路或控制協定中需要傳送三種報文,其格式分別為packetA、packetB、packetC:

struct structA
{
int a;
char b;
};

struct structB
{
char a;
short b;
};

struct structC
{
int a;
char b;
float c;
}
  優秀的程式設計者這樣設計傳送的報文:

struct CommuPacket
{
int iPacketType;  //報文類型標誌
union      //每次傳送的是三種報文中的一種,使用union
{
struct structA packetA;
struct structB packetB;
struct structC packetC;
}
};
  在進行報文傳送時,直接傳送struct CommuPacket一個整體。

  假設發送函數的原形如下:

// pSendData:發送位元組流的首位址,iLen:要發送的長度
Send(char * pSendData, unsigned int iLen);
發送方可以直接進行如下調用發送struct CommuPacket的一個實例sendCommuPacket:
Send( (char *)&sendCommuPacket , sizeof(CommuPacket) );
假設接收函數的原形如下:
// pRecvData:發送位元組流的首位址,iLen:要接收的長度
//返回值:實際接收到的位元組數
unsigned int Recv(char * pRecvData, unsigned int iLen);
  接收方可以直接進行如下調用將接收到的資料保存在struct CommuPacket的一個實例recvCommuPacket中:

Recv( (char *)&recvCommuPacket , sizeof(CommuPacket) );
  接著判斷報文類型進行相應處理:

switch(recvCommuPacket. iPacketType)
{
case PACKET_A:
… //A類報文處理
break;
case PACKET_B:
…  //B類報文處理
break;
case PACKET_C:
… //C類報文處理
break;
}
  以上程式中最值得注意的是

Send( (char *)&sendCommuPacket , sizeof(CommuPacket) );
Recv( (char *)&recvCommuPacket , sizeof(CommuPacket) );
  中的強制類型轉換:(char *)&sendCommuPacket、(char *)&recvCommuPacket,先取地址,再轉化為char型指標,這樣就可以直接利用處理位元組流的函數。

  利用這種強制類型轉化,我們還可以方便程式的編寫,例如要對sendCommuPacket所處記憶體初始化為0,可以這樣調用標準庫函數memset():

memset((char *)&sendCommuPacket,0, sizeof(CommuPacket));

2. struct的成員對齊
  Intel、微軟等公司曾經出過一道類似的面試題:

1. #include

2. #pragma pack(8)
3. struct example1
4. {
5. short a;
6. long b;
7. };

8. struct example2
9. {
10. char c;
11. example1 struct1;
12. short e;
13. };
14. #pragma pack()

15. int main(int argc, char* argv[])
16. {
17. example2 struct2;

18. cout << sizeof(example1) << endl;
19. cout << sizeof(example2) << endl;
20. cout << (unsigned int)(&struct2.struct1) - (unsigned int)(&struct2)
<< endl;

21. return 0;
22. }
  問程式的輸入結果是什麼?

  答案是:

8
16
4

  不明白?還是不明白?下麵一一道來:

2.1 自然對界

  struct是一種複合資料類型,其構成元素既可以是基本資料類型(如int、long、float等)的變數,也可以是一些複合資料類型(如array、struct、union等)的資料單元。對於結構體,編譯器會自動進行成員變數的對齊,以提高運算效率。缺省情況下,編譯器為結構體的每個成員按其自然對界(natural alignment)條件分配空間。各個成員按照它們被聲明的順序在記憶體中順序存儲,第一個成員的位址和整個結構的位址相同。

  自然對界(natural alignment)即默認對齊方式,是指按結構體的成員中size最大的成員對齊。

  例如:

struct naturalalign
{
char a;
short b;
char c;
};
  在上述結構體中,size最大的是short,其長度為2位元組,因而結構體中的char成員a、c都以2為單位對齊,sizeof(naturalalign)的結果等於6;

  如果改為:

struct naturalalign
{
char a;
int b;
char c;
};
  其結果顯然為12。

2.2指定對界

  一般地,可以通過下面的方法來改變缺省的對界條件:

  • 使用虛擬指令#pragma pack (n),編譯器將按照n個位元組對齊;
  • 使用虛擬指令#pragma pack (),取消自定義位元組對齊方式。

  注意:如果#pragma pack (n)中指定的n大於結構體中最大成員的size,則其不起作用,結構體仍然按照size最大的成員進行對界。

  例如:

#pragma pack (n)
struct naturalalign
{
char a;
int b;
char c;
};
#pragma pack ()
  當n為4、8、16時,其對齊方式均一樣,sizeof(naturalalign)的結果都等於12。而當n為2時,其發揮了作用,使得sizeof(naturalalign)的結果為8。

  在VC++ 6.0編譯器中,我們可以指定其對界方式,其操作方式為依次選擇projetct > setting > C/C++功能表,在struct member alignment中指定你要的對界方式。

  另外,通過__attribute((aligned (n)))也可以讓所作用的結構體成員對齊在n位元組邊界上,但是它較少被使用,因而不作詳細講解。

2.3 面試題的解答

  至此,我們可以對Intel、微軟的面試題進行全面的解答。

  程式中第2行#pragma pack (8)雖然指定了對界為8,但是由於struct example1中的成員最大size為4(long變數size為4),故struct example1仍然按4位元組對界,struct example1的size為8,即第18行的輸出結果;

  struct example2中包含了struct example1,其本身包含的簡單資料成員的最大size為2(short變數e),但是因為其包含了struct example1,而struct example1中的最大成員size為4,struct example2也應以4對界,#pragma pack (8)中指定的對界對struct example2也不起作用,故19行的輸出結果為16;

  由於struct example2中的成員以4為單位對界,故其char變數c後應補充3個空,其後才是成員struct1的記憶體空間,20行的輸出結果為4。

3. C和C++間struct的深層區別
  在C++語言中struct具有了“類” 的功能,其與關鍵字class的區別在於struct中成員變數和函數的默認訪問許可權為public,而class的為private。

  例如,定義struct類和class類:

struct structA
{
char a;

}
class classB
{
char a;

}
  則:

struct A a;
a.a = 'a'; //訪問public成員,合法
classB b;
b.a = 'a'; //訪問private成員,不合法
  許多文獻寫到這裏就認為已經給出了C++中struct和class的全部區別,實則不然,另外一點需要注意的是:

  C++中的struct保持了對C中struct的全面相容(這符合C++的初衷——“a better c”),因而,下面的操作是合法的:

//定義struct
struct structA
{
char a;
char b;
int c;
};
structA a = {'a' , 'a' ,1}; // 定義時直接賦初值
  即struct可以在定義的時候直接以{ }對其成員變數賦初值,而class則不能,在經典書目《thinking C++ 2nd edition》中作者對此點進行了強調。

4. struct編程注意事項
  看看下面的程式:

1. #include

2. struct structA
3. {
4. int iMember;
5. char *cMember;
6. };

7. int main(int argc, char* argv[])
8. {
9. structA instant1,instant2;
10.char c = 'a';

11. instant1.iMember = 1;
12. instant1.cMember = &c;

13.instant2 = instant1;

14.cout << *(instant1.cMember) << endl;

15.*(instant2.cMember) = 'b';

16. cout << *(instant1.cMember) << endl;

17. return 0;
}
  14行的輸出結果是:a
  16行的輸出結果是:b

  Why?我們在15行對instant2的修改改變了instant1中成員的值!

  原因在於13行的instant2 = instant1賦值語句採用的是變數逐個拷貝,這使得instant1和instant2中的cMember指向了同一片記憶體,因而對instant2的修改也是對instant1的修改。

  在C語言中,當結構體中存在指標型成員時,一定要注意在採用賦值語句時是否將2個實例中的指標型成員指向了同一片記憶體。

  在C++語言中,當結構體中存在指標型成員時,我們需要重寫struct的拷貝構造函數並進行“=”操作符重載。

轉載自CSDN

2007年4月24日 星期二

NTT DoCoMo Vision 2010 - Old School Friends


這是日本電信DoCoMo所拍的一支影片
是以通訊科技為主軸來描述2010年生活的影片
片長約12分鐘. 非常精彩!

很多事我們都知道可能
但沒人想過怎樣呈現
DoCoMo把未來一部份描繪出來了
影片是2004年拍的,你相信六年後的世界會是這樣嗎
如今又過那麼多年了
也許我們五年後真的可以擁有那樣的生活環境:)

不過我想6年後日本人的應予應該不會就變成這麼標準吧

約十五分鐘,一定要看到最後,最後才是最感性的。
未來的行動電話,各位可以在這支影片中看得到唷!
未來十年的科技,很值得一看
NTT DoCoMo 另一支形象片
也是講10年後 Mobil Network 的應用
這是日本 DOCOMO所拍的廣告片拍的相當的好~
裡面所談的是未來十年的的通訊及生活

轉貼自:http://avdio.blogspot.com/2007/04/ntt-docomo-vision-2010.html

DoCoMo Corporate Videos

2007年4月12日 星期四

楊宗緯 人質@超級星光大道2007.4.6

比較一下楊宗緯跟張惠妹唱的版本有何不同吧!個人是覺得都唱得很棒呢^^




http://www.im.tv/vlog/Personal/23450/1392161

2007年4月10日 星期二

mangos ZG技能資料

古拉巴什擲斧者 利斧亂舞
哈卡萊祭司 範圍恐懼,魔法免疫護盾,死後冒出巫毒之魂
哈卡萊飲血者 範圍攻擊並附帶吸血效果,每攻擊到一個玩家會吸1500HP
哈卡萊勇士 衝鋒技能
暗影獵手 亂射
哈卡萊高階祭司 會幫同伴進行持續性的補血
哈卡萊暗影祭司 會使用法力燃燒和暗影箭雨,箭雨傷約1000左右
古拉巴什獵頭者 橫掃攻擊和遠距離擲斧...皺縮詛咒
古拉巴什狂暴者 平時移動很慢,進入戰鬥後會變超快,會使用範圍擊飛,範圍恐懼

拉沙希奎蛇 會使用毒液噴射,每2秒造成100點傷害
拉沙希毒蛇 寧神毒藥,穿刺護甲
覓血蝙蝠 衝鋒
自暴蝙蝠
拉札希毒液蜘蛛 減速毒藥,興奮毒液
拉札希寡婦蜘蛛 包覆之網,死掉後會再生4支拉札希誘捕者
拉札希誘捕者 沒什麼特別技能,移動速度超快,一次4支的團體行動
拉札希迅猛龍 感染撕咬
祖利安雌獵虎 4-6支成群
祖利安徘徊者
祖利安幼虎
祖利安猛虎
祖利安鱷魚 緩速技
眼鏡王蛇 穿刺護甲
大蛇 吸取生命,被吸取者會進入沉默狀態
哈卡之子 範圍擊倒,死後噴擴散毒雲

侏儒 火雨,召喚煉獄魔
衰老的侍女 驅散魔法,邪惡狂暴
阿塔萊侍女 痛擊,血之詛咒
==========================================================================
1王蝙蝠王
HP 50%以上蝙蝠型態 50%以下食人妖型態
第一階段
衝鋒,範圍沉默,蝠群召喚
第二階段
血之詛咒,治療術,暗言術:痛,燃燒彈攻擊

對白:
我命令你把這些入侵者燒成灰燼!
開始施放強效治療術!

2王蛇王
HP 50%以上食人妖型態 50%以下蛇型態
第一階段
神聖怒火,連鎖新星
第二階段
毒雲術

3王蜘蛛王
開場:食人妖型態 血量80% 之後會在2種型態間不定時轉換
人型:毒液噴灑,召喚蜘蛛,吸血
蜘蛛:灑網

對白:
來幫助我吧,我的孩子們!
沙德拉,讓我成為你的化身!

4王血領主曼多基爾
無限升級-只要一有玩家死亡就獲得exp然後漸漸升級
狂怒
衝鋒+旋風斬
威懾凝視(對白:xxx!我正在監視你)

5王虎王
分2階段(1)3王之亂(2)虎王再臨
第一階段
古拉巴什食腐者 - 致死打擊+暴擊
狂熱者札斯 - 鑿擊
狂熱者洛卡恩 - 閃電盾 補血
3支需在10秒內同時一起死亡,否則互相復活
第二階段
虎王跳躍攻擊:40碼內玩家皆受到衝擊波傷害,會跌倒,中斷施法

6王豹王
第一階段:源源不絕的黑豹群 第二階段:神出鬼沒的豹王
第一階段
暗言術,旋風斬,印記
第二階段
消失

對白:
吞噬xxx的軀體吧,我的小可愛們!

7王哈卡
心靈控制
生命虹吸
腐化之血
減速
狂怒

對白:
驕傲會將你送上絕路。來吧,凡人!品嘗靈魂掠奪者的憤怒吧!


8王金度
治療圖騰
洗腦圖騰
召喚鬼影
傳送
變形-把人變青蛙

妖術師金度施放了召喚洗腦圖騰。
妖術師金度施放了強力治療結界。
^(.+)受到(.*)金度的欺騙 <--不知道全句是什麼@@

2007年4月9日 星期一

女兒學會了"噗"

2007/04/07 清明節那天,我老婆跟她妹妹一起回娘家去,一回台北發現了一件事
女兒學會了一項新技能,"噗",女兒的小阿姨教她的@@

結果她現在三不五時就一直"噗"...真是可愛啊...哇哈哈

2007年4月7日 星期六

mangos 奧妮克希亞的AI

這基本上都是ScriptDev2裏的程式碼,我改成中文的
測試的感覺是,如果公主第2階段飛在空中時能再飛高一點,換位置時不要跑到地面上...就蠻不錯的了^^
還有召小龍的功能沒有@@
.addspawn 10184 <==如果進副本沒發現奧妮克西亞時,試試看這個指令能不能把它請出來^^

17136 變身成奧妮克希亞的外觀
17647 Create Onyxia Spawner(沒用@@)
18391 Onyxia Speed Burst(也沒什麼作用@@)
20279 召喚玩家 召喚一個敵人來面對奧妮克希亞的怒火
22905 放置未淬火的劍(我不知道這做什麼用的@@)
22906 刺殺奧妮克希亞(我不知道這做什麼用的@@)
18387 奧妮克希亞(點了也是沒反應@@)
18596 吐息 會冒煙的吐息術喔
21139 火息術 不會冒煙的吐火
11010 懸浮 變成龍可以飛蠻高的
17131 懸浮 好像不能用

//scripts/zone/onyxias_lair/sc_boss_onyxia.cpp
#include "../../sc_defines.h"

// **** 這個script尚在開發中 ****

#define SPELL_WINGBUFFET 18500 //龍翼攻擊 錐形區域造成563-937的傷害並擊退
#define SPELL_FLAMEBREATH 18435 //火息術
#define SPELL_CLEAVE 26350 //順劈斬
#define SPELL_TAILSWEEP 15847 //25653 英文版的龍尾掃擊 15847中文版
#define SPELL_FIREBALL 20692 //不用法力的火球術
#define SPELL_DEEPBREATH 18596 //吐息術,火息術 21139好像也行
#define SPELL_BELLOWINGROAR 18431 //低沉咆哮 大範圍的恐懼

//召喚雛龍功能尚未被核心實作出來
#define SPELL_SUMMONWHELP 17646 //10碼的召喚奧妮克希亞雛龍
#define SPELL_SUMMON_MULTI_WHELPS 20171 //召喚奧妮克希亞雛龍(被動技)

//#define SAY_AGGRO "How fortuitous. Usually, I must leave my lair to feed."
//#define SAY_KILL "Learn your place mortal!"
//#define SAY_PHASE_2_TRANS "I'll incinerate you from above!"
//#define SAY_PHASE_3_TRANS "It seems you'll need another lesson"
#define SAY_AGGRO "真幸運,通常我要離開巢穴才能找到食物."
#define SAY_KILL "要知道你的處境,凡人!"
#define SAY_PHASE_2_TRANS "這毫無意義的行動讓我很厭煩。我會從上空把你們都燒成灰!"
#define SAY_PHASE_3_TRANS "看起來需要再給你一次教訓,凡人!"

struct MANGOS_DLL_DECL boss_onyxiaAI : public ScriptedAI
{
boss_onyxiaAI(Creature *c) : ScriptedAI(c) {Reset();}

uint32 swingcounter;
uint32 flamebreath_timer;
uint32 cleave_timer;
uint32 tailsweep_timer;
uint32 movement_timer;
uint32 fireball_timer;
uint32 whelpspawn_timer;
uint32 bellowingroar_timer;
uint32 reset_timer;
uint32 phase;

void Reset()
{
swingcounter = 0;
flamebreath_timer = 20000;
cleave_timer = 15000;
tailsweep_timer = 10000;
movement_timer = 10000;
fireball_timer = 5000;
whelpspawn_timer = 45000;
bellowingroar_timer = 0;
phase = 1;

if (m_creature)
{
m_creature->InterruptSpell();
m_creature->SetHover(false);
(*m_creature)->Clear(false);
EnterEvadeMode();
}
}

void KilledUnit(Unit* victim)
{
if (rand()%5) //亂數決定是否要公主說話
return;

DoYell(SAY_KILL,LANG_UNIVERSAL,NULL);
}

void AttackStart(Unit *who)
{
if (!who)
return;

if (m_creature->getVictim() == NULL && who->isTargetableForAttack() && who != m_creature)
{
//開始攻擊
DoStartMeleeAttack(who);

//最一開始攻擊時,黑龍公主說的第一句話
DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL);
}
}

void MoveInLineOfSight(Unit *who)
{
if (!who || m_creature->getVictim())
return;

if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who))
{
float attackRadius = m_creature->GetAttackDistance(who);
if (m_creature->IsWithinDist(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE)
{
if(who->HasStealthAura())
who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);

//開始攻擊
DoStartMeleeAttack(who);

//最一開始攻擊時,黑龍公主說的第一句話
DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL);
}
}
}

void UpdateAI(const uint32 diff)
{
//If we had a target and it wasn't cleared then it means the target died from some unknown soruce
//But we still need to reset
if (!m_creature->SelectHostilTarget())
{
Reset();//黑龍公主重置
return;
}

//檢查是否有玩家存活著
if( m_creature->getVictim() && m_creature->isAlive())
{
//第1及第3階段的施法
if (phase == 1 || phase == 3)
{
//每15秒1次火焰吐息
if (flamebreath_timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_FLAMEBREATH);
flamebreath_timer = 15000;

}else flamebreath_timer -= diff;

//每20秒一次順劈斬
if (cleave_timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_CLEAVE);
cleave_timer = 20000;

}else cleave_timer -= diff;

//每10秒神龍擺尾一次
if (tailsweep_timer < diff)
{
//如果有人在公主擺尾的範圍內,則進行神龍擺尾的特攻
if (!m_creature->HasInArc( M_PI, m_creature->getVictim()))
DoCast(m_creature->getVictim(),SPELL_TAILSWEEP);

tailsweep_timer = 20000;

}else tailsweep_timer -= diff;
}

//第2階段
if (phase == 2)
{
//如果不是在盤旋的話,則飛起來
if (!m_creature->isHover())
{
m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
m_creature->SetHover(true);
}

//每10秒亂數移動位置一次
if (movement_timer < diff)
{
//不管是否在施法中一律中斷並移動到亂數選到的位置去
m_creature->InterruptSpell();
uint32 position = rand()%9;

switch (position)
{
case 0:
SpecialMove(-65.8444,-213.809,-84.2985,5000);
m_creature->Relocate(-65,-213,-84,0);
break;
case 1:
SpecialMove(22.8739,-217.152,-85.0548,5000);
m_creature->Relocate(22,-217,-85,0);
break;
case 2:
SpecialMove(-33.5561,-182.682,-88.9457,5000);
m_creature->Relocate(-33,-182,-88,0);
break;
case 3:
SpecialMove(-31.4963,-250.123,-89.1278,5000);
m_creature->Relocate(-31,-250,-89,0);
break;
case 4:
SpecialMove(-2.78999,-181.431,-86.8962,5000);
m_creature->Relocate(-2,-181,-86,0);
break;
case 5:
SpecialMove(-54.9415,-232.242,-85.5555,5000);
m_creature->Relocate(-54,-232,-85,0);
break;
case 6:
SpecialMove(-65.2653,-194.879,-84.6718,5000);
m_creature->Relocate(-65,-194,-84,0);
break;
case 7:
SpecialMove(10.5665,-241.478,-85.9426,5000);
m_creature->Relocate(10,-241,-85,0);
break;
case 8:
//1/9的機會施展一次深呼,否則的話移動位置
//DoTextEmote("takes a deep breath...",NULL);
//DoYell("Deep Breath would go here!", LANG_UNIVERSAL,NULL);
DoTextEmote("深深地吸了一口氣...",NULL);
DoYell("奧妮克希亞深呼吸即將出現!", LANG_UNIVERSAL,NULL);
break;
}

DoCast(m_creature,11010);//懸浮?
movement_timer = 10000;
}else movement_timer -= diff;

//每5秒亂數決定吐火球
if (fireball_timer < diff)
{
//應該要隨機選一個目標但是因沒有仇恨列表所以直接對現有的目標進行攻擊
DoCast(m_creature->getVictim(),SPELL_FIREBALL);
fireball_timer = 5000;
}else fireball_timer -= diff;

//每45秒重生一次小龍
if (whelpspawn_timer < diff)
{
//不過核心還不支援所以只顯示重生訊息
DoYell("小龍重生!",LANG_UNIVERSAL,NULL); //召喚小龍還沒有實作
whelpspawn_timer = 450000;
}else whelpspawn_timer -= diff;
}

//第3階段才會出現的特攻
if (phase == 3)
{
//每10-30秒出現一次
if (bellowingroar_timer < diff && phase == 3)
{
//大範圍恐懼,如果沒修正恐懼Bug的話請將下一行拿掉
DoCast(m_creature->getVictim(),SPELL_BELLOWINGROAR);
bellowingroar_timer = 10000 + rand()%20000;
}else bellowingroar_timer -= diff;
}

//第1階段到第2階段轉換時機為血低於60%
if ( phase == 1 && (m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 60)
{
phase = 2;
m_creature->InterruptSpell();
m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
(*m_creature)->Clear(false);
(*m_creature)->Idle();
DoCast(m_creature,11010);//懸浮?...
m_creature->SetHover(true);
DoCast(m_creature,18430);//公主空中盤旋? 學到的技能是龍類懸浮@@
DoYell(SAY_PHASE_2_TRANS,LANG_UNIVERSAL,NULL);
}

//第2階段到第3階段轉換時機為血低於40%
if ( phase == 2 && (m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 40)
{
phase = 3;
m_creature->InterruptSpell();
//m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,EMOTE_STATE_STAND);
m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
m_creature->SetHover(false);
(*m_creature)->Clear(false);
(*m_creature)->Mutate(new TargetedMovementGenerator(*m_creature->getVictim()));
DoYell(SAY_PHASE_3_TRANS,LANG_UNIVERSAL,NULL);
}

//如果正處於跟公主的混戰範圍又不在第二階段
if( phase!=2 && m_creature->getVictim() && m_creature->IsWithinDist(m_creature->getVictim(), ATTACK_DIST))
{
if( m_creature->isAttackReady() )
{
m_creature->AttackerStateUpdate(m_creature->getVictim());
m_creature->resetAttackTimer();

//在第1階段及第3階段每12次攻擊後做出一次龍翼打擊
if (swingcounter > 12 && m_creature->getVictim())
{
DoCast(m_creature->getVictim(),SPELL_WINGBUFFET);
swingcounter = 0;
}else swingcounter++;
}
}
}
}

void SpecialMove(float X, float Y, float Z, uint32 Time)
{
WorldPacket data( SMSG_MONSTER_MOVE, (41+m_creature->GetPackGUID().size()) );
data.append(m_creature->GetPackGUID());

data << m_creature->GetPositionX() << m_creature->GetPositionY() << m_creature->GetPositionZ();
// unknown field - unrelated to orientation
// seems to increment about 1000 for every 1.7 seconds
// for now, we'll just use mstime
data << getMSTime();

data << uint8(0); // walkback when walking from A to B
data << uint32(0x0200); // 旗標
/* 旗標:
512: 漂著, 或不是以跑或走的型態移動
*/
data << Time; // Time in between points
data << uint32(1); // 1 single waypoint
data << X << Y << Z; // the single waypoint Point B
m_creature->SendMessageToSet( &data, true );
}
};
CreatureAI* GetAI_boss_onyxiaAI(Creature *_Creature)
{
return new boss_onyxiaAI (_Creature);
}


void AddSC_boss_onyxia()
{
Script *newscript;
newscript = new Script;
newscript->Name="boss_onyxia";
newscript->GetAI = GetAI_boss_onyxiaAI;
m_scripts[nrscripts++] = newscript;
}

2007年4月6日 星期五

mangos 2.0.10 patch

還不知道能不能用,不過感覺上如果patch了...資料庫似乎要變動蠻大的@@等過些時候再來研究吧@@
而且好像並沒有實作的很完整,作者有提到說是很粗糙的patch的樣子唷!

Known bugs :-
- Item template query still needs to be fixed (will do shortly)
- Quest query still needs to be fixes
- Cubes/boxes on npcs (needs a field pushed forward)
- Authpacket change (needs to be done to enable new races)

Known working :- (Fixed stuff)
- Groups
- Channels
- Full 2.0.10 support
- No advertisement bullshit
- DBC structures updated to 2.0.x

I'll post a new patch shortly with the item/quest stuff fixed, as at the moment I am extremely busy with quite a few things on my mind (mainly my own server/emu, which has just undergone a lot of massive core changes).

I repeat: this is *NOT* EspireEmu/mmorpg4free's emu. That is my other (main) project that I spend my time on. This is merely an effort on my part to correct what I may have offended some members of the community and to help you guys out.

Download link: http://filebeam.com/33dd97a00fc6c7b12c0db2610bf57b51

* This is a source .patch file. It is not a binary. I'll leave compiling up to you guys, providing binaries in my opinion is bad because it leaves the way to viruses/trojans. Simply apply this patch to the LATEST svn, and recompile, and you should be good. *

*** UPDATE ***

Fixed:
* Item prototypes -> should fix encanting etc professions, use my item sql.
* Creature templates -> fixes all cubes on mobs, use my creature sql.
* TBC Races enable
* Quest query packet
* Item query packet
* Negative aura cancel

creature_template.sql -> http://filebeam.com/919ba963cb981e61a13228016a27a66f
item_template.sql -> http://filebeam.com/49c95f98b5290e096dad442f8acfca14
mangos-2.0.10-2.rar -> http://filebeam.com/61f9a9501462cd4eb032ef5d85ccf7c8

原文出處:
http://forum.ragezone.com/world-warcraft/mangos-v2-0-10-burning-crusade-server-228833.html

時間=金錢,智識=力量

中國人說:時間就是金錢(Time = Money)
又說:智識就是力量(Knowledge = Power)

由基本物理智識得知:
Power = Work / Time

將最上面的2公式代入
Knowledge = Work / Money => Money = Work / Knowledge

所以就是當knowledge = 0時 則 Money => ∞ <===當你什麼都不懂時你會變得很有錢
可是當你的Money = 0時,則Knowledge => ∞ <===當你沒錢時,為了生活,你還是必需努力的吸收智識
===============================================
結論:請常唸 六字大明咒『嗡 嘛 尼 唄 咩 吽』 => 『all money back me home』

有趣的計算

如果用A、B、C、D……X、Y、Z這26個英文字母,分別等於百分之1、2、3、4……24、25、26這26個數值,那麼我們就能得出如下有趣的結論:
HARDWORK(努力工作):H+A+R+D+W+O+R+K=8+1+18+4+23+15+18+11=98%
KNOWLEDGE(知識):K+N+O+W+L+E+D+G+E=11+14+15+23+12+5+4+7+5=96%
LOVE(愛情):L+O+V+E=12+15+22+5=54%
LUCK(好運):L+U+C+K=12+21+3+11=47%
些對我們通常非常看重的東西都不是最完滿的,雖然它們非常重要,那麼,究竟什麼能使得生活變的圓滿?
是MONEY(金錢)嗎? 不! M+O+N+E+Y=13+15+14+5+25=72%
是LEADERSHIP(領導能力)嗎? 不! L+E+A+D+E+R+S+H+I+P=12+5+1+4+5+18+19+8+9+16=97%
是SEX(性)嗎? 更不是! S+E+X=19+5+24=48%
那麼,什麼能使生活變成100%的圓滿呢?
是ATTITUDE(心態)。 A+T+T+I+T+U+D+E=1+20+20+9+20+21+4+5=100%
正是我們對待工作、生活的態度能夠使我們的生活達到100%的圓滿!

SUN公司的考試題目

據說是一道SUN公司的考試題目
題目如下:
寫出一個帶參數的巨集#define get_struct_addr_from_member_addr(p,stru,m)
這個巨集能根據任意的結構實體的某一個成員位址,算出該結構實體的位址
其中參數p是指向該成員的指標,stru結構實體,m是該成員名稱


本題答案:
#define get_struct_addr_from_member_addr(p, stru, m) \
(stru*)( (char*)p - (char*)&(((stru*)0)->m) )

這一題的答案,最怪的部份就是((stru*)0)->m了,居然有這種寫法@@
因為照理說根本不可能在位址為0的地方找到m的位址啊!

可是實際上是..編譯器在進行((stru*)0)->m的運算時,要先取得stru的位置才能取到m的位址,
而在m不存在時,位置就不會改變,於是結果就得到了stru的位置了@@
======================================================
//--winnt.h
// Calculate the address of the base of the structure given its type, and an
// address of a field within the structure.
//
#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(UINT_PTR)(&((type *)0)->field)))

有趣的C程式

#include <stdio.h>
int main(void)
{
char *c="#include <stdio.h>%cint main(void)%c{%cchar *c=%c%s%c;%cprintf(c,10,10,10,34,c,34,10,10);%c}";
printf(c,10,10,10,34,c,34,10,10);
}

有沒有看懂這段程式在做什麼呀?想一下吧^^

電腦開機流程

自 MBR 一路開機到 Linux 第一個 user-space process 的過程

典型 x86 Linux 啟動程序

典型 ARM / PowerPC Linux 啟動程序

發信人: suzhe (I Love Linux), 信區: FreeDevelop
標 題: HardDisk,Partition,Boot,OSLoader專題
發信站: BBS 水木清華站 (Sat Nov 20 16:45:44 1999)

第一部分 簡 介
1,1
一. 硬碟結構簡介

1. 硬碟參數釋疑

到目前為止, 人們常說的硬碟參數還是古老的 CHS (Cylinder/
Head/Sector)參數. 那麼為什麼要使用這些參數, 它們的意義是什麼?
它們的取值範圍是什麼?
很久以前, 硬碟的容量還非常小的時候, 人們採用與軟碟類似的結
構生產硬碟. 也就是硬碟盤片的每一條磁道都具有相同的扇區數. 由此
產生了所謂的3D參數 (Disk Geometry). 既磁頭數(Heads), 柱面數
(Cylinders), 扇區數(Sectors),以及相應的尋址方式.

其中:

磁頭數(Heads) 表示硬碟總共有幾個磁頭,也就是有幾面盤片, 最大
為 255 (用 8 個二進製位存儲);
柱面數(Cylinders) 表示硬碟每一面盤片上有幾條磁道, 最大為 1023
(用 10 個二進製位存儲);
扇區數(Sectors) 表示每一條磁道上有幾個扇區, 最大為 63 (用 6
個二進製位存儲).
每個扇區一般是 512個位元組, 理論上講這不是必須的, 但好象沒有取
別的值的.

所以磁碟最大容量為:

255 * 1023 * 63 * 512 / 1048576 = 8024 GB ( 1M = 1048576 Bytes )
或硬碟廠商常用的單位:
255 * 1023 * 63 * 512 / 1000000 = 8414 GB ( 1M = 1000000 Bytes )

在 CHS 尋址方式中, 磁頭, 柱面, 扇區的取值範圍分別為 0 到 Heads - 1,
0 到 Cylinders - 1, 1 到 Sectors (注意是從 1 開始).

2. 基本 Int 13H 調用簡介

BIOS Int 13H 調用是 BIOS 提供的磁碟基本輸入輸出中斷調用, 它可以
完成磁碟(包括硬碟和軟碟)的復位, 讀寫, 校驗, 定位, 診斷, 格式化等功能.
它使用的就是 CHS 尋址方式, 因此最大識能訪問 8 GB 左右的硬碟 ( 本文中
如不作特殊說明, 均以 1M = 1048576 位元組為單位).

3. 現代硬碟結構簡介

在老式硬碟中, 由於每個磁道的扇區數相等, 所以外道的記錄密度要遠低
於內道, 因此會浪費很多磁碟空間 (與軟碟一樣). 為了解決這一問題, 進一
步提高硬碟容量, 人們改用等密度結構生產硬碟. 也就是說, 外圈磁道的扇區
比內圈磁道多. 採用這種結構後, 硬碟不再具有實際的3D參數, 尋址方式也改
為線性尋址, 即以扇區為單位進行尋址.
為了與使用3D尋址的老軟體兼容 (如使用BIOS Int13H接口的軟體), 在硬
盤控制器內部安裝了一個地址翻譯器, 由它負責將老式3D參數翻譯成新的線性
參數. 這也是為什麼現在硬碟的3D參數可以有多種選擇的原因 (不同的工作模
式, 對應不同的3D參數, 如 LBA, LARGE, NORMAL).

4. 擴展 Int 13H 簡介

雖然現代硬碟都已經採用了線性尋址, 但是由於基本 Int 13H 的制約, 使
用 BIOS Int 13H 接口的程式, 如 DOS 等還只能訪問 8 G 以內的硬碟空間.
為了打破這一限制, Microsoft 等幾家公司制定了擴展 Int 13H 標準
(Extended Int13H), 採用線性尋址方式存取硬碟, 所以突破了 8 G 的限制,
而且還加入了對可拆卸介質 (如活動硬碟) 的支持.

二. Boot Sector 結構簡介

1. Boot Sector 的組成

Boot Sector 也就是硬碟的第一個扇區, 它由 MBR (Master Boot Record),
DPT (Disk Partition Table) 和 Boot Record ID 三部分組成.

MBR 又稱作主引導記錄佔用 Boot Sector 的前 446 個位元組 ( 0 to 0x1BD ),
存放系統主引導程式 (它負責從活動分區中裝載並運行系統引導程式).
DPT 即主分區表佔用 64 個位元組 (0x1BE to 0x1FD), 記錄了磁碟的基本分區
資訊. 主分區表分為四個分區項, 每項 16 位元組, 分別記錄了每個主分區的資訊
(因此最多可以有四個主分區).
Boot Record ID 即引導區標記佔用兩個位元組 (0x1FE and 0x1FF), 對於合法
引導區, 它等於 0xAA55, 這是判別引導區是否合法的標誌.
Boot Sector 的具體結構如下圖所示 (參見 NightOwl 大俠的文章):
      0000  |------------------------------------------------|
| |
| |
| Master Boot Record |
| |
| |
| 主引導記錄(446位元組) |
| |
| |
| |
01BD | |
01BE |------------------------------------------------|
| |
01CD | 分區資訊 1(16位元組) |
01CE |------------------------------------------------|
| |
01DD | 分區資訊 2(16位元組) |
01DE |------------------------------------------------|
| |
01ED | 分區資訊 3(16位元組) |
01EE |------------------------------------------------|
| |
01FD | 分區資訊 4(16位元組) |
|------------------------------------------------|
| 01FE | 01FF |
| 55 | AA |
|------------------------------------------------|

2. 分區表結構簡介

分區表由四個分區項構成, 每一項的結構如下:

BYTE State : 分區狀態, 0 = 未激活, 0x80 = 激活 (注意此項)
BYTE StartHead : 分區起始磁頭號
WORD StartSC : 分區起始扇區和柱面號, 底位元組的低6位為扇區號,
高2位為柱面號的第 9,10 位, 高位元組為柱面號的低 8 位
BYTE Type : 分區類型, 如 0x0B = FAT32, 0x83 = Linux 等,
00 表示此項未用
BYTE EndHead : 分區結束磁頭號
WORD EndSC : 分區結束扇區和柱面號, 定義同前
DWORD Relative : 在線性尋址方式下的分區相對扇區地址
(對於基本分區即為絕對地址)
DWORD Sectors : 分區大小 (總扇區數)

注意: 在 DOS / Windows 系統下, 基本分區必須以柱面為單位劃分
( Sectors * Heads 個扇區), 如對於 CHS 為 764/255/63 的硬碟, 分區的
最小尺寸為 255 * 63 * 512 / 1048576 = 7.844 MB.

3. 擴展分區簡介

由於主分區表中只能分四個分區, 無法滿足需求, 因此設計了一種擴展
分區格式. 基本上說, 擴展分區的資訊是以鏈表形式存放的, 但也有一些特
別的地方.
首先, 主分區表中要有一個基本擴展分區項, 所有擴展分區都隸屬於它,
也就是說其他所有擴展分區的空間都必須包括在這個基本擴展分區中. 對於
DOS / Windows 來說, 擴展分區的類型為 0x05.
除基本擴展分區以外的其他所有擴展分區則以鏈表的形式級聯存放, 後
一個擴展分區的數據項記錄在前一個擴展分區的分區表中, 但兩個擴展分區
的空間並不重疊.
擴展分區類似於一個完整的硬碟, 必須進一步分區才能使用. 但每個擴
展分區中只能存在一個其他分區. 此分區在 DOS/Windows 環境中即為邏輯盤.
因此每一個擴展分區的分區表 (同樣存儲在擴展分區的第一個扇區中)中最多
只能有兩個分區數據項(包括下一個擴展分區的數據項).
擴展分區和邏輯盤的示意圖如下:
  |-----------------------|             --------
| 主擴展分區(/dev/hda2) | ^
|-----------------------| |
| 擴 展 | 分區項 1 |-- |
| |------------| | |
| 分區表 | 分區項 2 |--+-- |
|-----------------------| | | |
| | | | |
| 邏輯盤 1 (/dev/hda5) |<-/ | |
| | | |
|-----------------------| | 主
| 擴展分區 2 |<----/
|-----------------------| 擴
| 擴 展 | 分區項 1 |--
| |------------| | 展
| 分區表 | 分區項 2 |--+--
|-----------------------| | | 分
| | | |
| 邏輯盤 2 (/dev/hda6) |<-/ | 區
| | | |
|-----------------------| | |
| 擴展分區 3 |<----/ |
|-----------------------| |
| 擴 展 | 分區項 1 |-- |
| |------------| | |
| 分區表 | 分區項 2 | | |
|-----------------------| | |
| | | |
| 邏輯盤 3 (/dev/hda7) |<-/ |
| | |
|-----------------------| ---------

三. 系統啟動過程簡介

系統啟動過程主要由一下幾步組成(以硬碟啟動為例):

1. 開機 :-)
2. BIOS 加電自檢 ( Power On Self Test -- POST )
內存地址為 0ffff:0000
3. 將硬碟第一個扇區 (0頭0道1扇區, 也就是Boot Sector)
讀入內存地址 0000:7c00 處.
4. 檢查 (WORD) 0000:7dfe 是否等於 0xaa55, 若不等於
則轉去嘗試其他啟動介質, 如果沒有其他啟動介質則顯示
"No ROM BASIC" 然後死機.
5. 跳轉到 0000:7c00 處執行 MBR 中的程式.
6. MBR 首先將自己複製到 0000:0600 處, 然後繼續執行.
7. 在主分區表中搜索標誌為活動的分區. 如果發現沒有活動
分區或有不止一個活動分區, 則轉停止.
8. 將活動分區的第一個扇區讀入內存地址 0000:7c00 處.
9. 檢查 (WORD) 0000:7dfe 是否等於 0xaa55, 若不等於則
顯示 "Missing Operating System" 然後停止, 或嘗試
軟碟啟動.
10. 跳轉到 0000:7c00 處繼續執行特定系統的啟動程式.
11. 啟動系統 ...

以上步驟中 2,3,4,5 步是由 BIOS 的引導程式完成. 6,7,8,9,10
步由MBR中的引導程式完成.

一般多系統引導程式 (如 SmartFDISK, BootStar, PQBoot 等)
都是將標準主引導記錄替換成自己的引導程式, 在運行系統啟動程式
之前讓用戶選擇要啟動的分區.
而某些系統自帶的多系統引導程式 (如 lilo, NT Loader 等)
則可以將自己的引導程式放在系統所處分區的第一個扇區中, 在 Linux
中即為 SuperBlock (其實 SuperBlock 是兩個扇區).

注: 以上各步驟中使用的是標準 MBR, 其他多系統引導程式的引導
過程與此不同.


第二部分 技術資料
第一章 擴展 Int13H 技術資料

一. 簡介
設計擴展 Int13H 接口的目的是為了擴展 BIOS 的功能, 使其支持
多於1024柱面的硬碟, 以及可移動介質的瑣定, 解鎖及彈出等功能.

二. 數據結構

1. 數據類型約定
BYTE 1 位元組整型 ( 8 位 )
WORD 2 位元組整型 ( 16 位 )
DWORD 4 位元組整型 ( 32 位 )
QWORD 8 位元組整型 ( 64 位 )

2. 磁碟地址數據包 Disk Address Packet (DAP)
DAP 是基於絕對扇區地址的, 因此利用 DAP, Int13H 可以輕鬆地逾
越 1024 柱面的限制, 因為它根本就不需要 CHS 的概念.
DAP 的結構如下:
    struct DiskAddressPacket
{
BYTE PacketSize; // 數據包尺寸(16位元組)
BYTE Reserved; // ==0
WORD BlockCount; // 要傳輸的數據塊個數(以扇區為單位)
DWORD BufferAddr; // 傳輸緩衝地址(segment:offset)
QWORD BlockNum; // 磁碟起始絕對塊地址
};


PacketSize 保存了 DAP 結構的尺寸, 以便將來對其進行擴充. 在
目前使用的擴展 Int13H 版本中 PacketSize 恆等於 16. 如果它小於
16, 擴展 Int13H 將返回錯誤碼( AH=01, CF=1 ).
BlockCount 對於輸入來說是需要傳輸的數據塊總數, 對於輸出來說
是實際傳輸的數據塊個數. BlockCount = 0 表示不傳輸任何數據塊.
BufferAddr 是傳輸數據緩衝區的 32 位地址 (段地址:偏移量). 數據
緩衝區必須位於常規內存以內(1M).
BlockNum 表示的是從磁碟開始算起的絕對塊地址(以扇區為單位),
與分區無關. 第一個塊地址為 0. 一般來說, BlockNum 與 CHS 地址的關係
是:
    BlockNum = cylinder * NumberOfHeads +
head * SectorsPerTrack +
sector - 1;

其中 cylinder, head, sector 是 CHS 地址, NumberOfHeads 是磁碟
的磁頭數, SectorsPerTrack 是磁碟每磁道的扇區數.
也就是說 BlockNum 是沿著 扇區->;磁道->;柱面 的順序記數的. 這一順
序是由磁碟控制器虛擬的, 磁碟表面數據塊的實際排列順序可能與此不同
(如為了提高磁碟速度而設置的間隔因子將會打亂扇區的排列順序).

3. 驅動器參數數據包 Drive Parameters Packet
驅動器參數數據包是在擴展 Int13H 的取得驅動器參數子功能調用中
使用的數據包. 格式如下:
    struct DriveParametersPacket
{
WORD InfoSize; // 數據包尺寸 (26 位元組)
WORD Flags; // 資訊標誌
DWORD Cylinders; // 磁碟柱面數
DWORD Heads; // 磁碟磁頭數
DWORD SectorsPerTrack; // 每磁道扇區數
QWORD Sectors; // 磁碟總扇區數
WORD SectorSize; // 扇區尺寸 (以位元組為單位)
};
資訊標誌用于返回磁碟的附加資訊, 每一位的定義如下:

0 位:
0 = 可能發生 DMA 邊界錯誤
1 = DMA 邊界錯誤將被透明處理
如果這位置 1, 表示 BIOS 將自動處理 DMA 邊界錯誤, 也就是說
錯誤代碼 09H 永遠也不會出現.

1 位:
0 = 未提供 CHS 資訊
1 = CHS 資訊合法
如果塊設備的傳統 CHS 幾何資訊不適當的話, 該位將置 0.

2 位:
0 = 驅動器不可移動
1 = 驅動器可移動

3 位: 表示該驅動器是否支持寫入時校驗.

4 位:
0 = 驅動器不具備介質更換檢測線
1 = 驅動器具備介質更換檢測線

5 位:
0 = 驅動器不可鎖定
1 = 驅動器可以鎖定
要存取驅動器號大於 0x80 的可移動驅動器, 該位必須置 1
(某些驅動器號為 0 到 0x7F 的設備也需要置位)

6 位:
0 = CHS 值是當前存儲介質的值 (僅對於可移動介質), 如果
驅動器中有存儲介質, CHS 值將被返回.
1 = CHS 值是驅動器支持的最大值 (此時驅動器中沒有介質).

7 - 15 位: 保留, 必須置 0.


三. 接口規範

1. 寄存器約定
在擴展 Int13H 調用中一般使用如下寄存器約定:

ds:di ==>; 磁碟地址數據包( disk address packet )
dl ==>; 驅動器號
ah ==>; 功能代碼 / 返回碼

在基本 Int13H 調用中, 0 - 0x7F 之間的驅動器號代表可移動驅動器
0x80 - 0xFF 之間的驅動器號代表固定驅動器. 但在擴展 Int13H 調用中
0x80 - 0xFF 之間還包括一些新出現的可移動驅動器, 比如活動硬碟等.
這些驅動器支持先進的鎖定,解鎖等功能.
ah 返回的錯誤碼除了標準 Int13H 調用規定的基本錯誤碼以外,又增加
了以下錯誤碼:

B0h 驅動器中的介質未被鎖定

B1h 驅動器中的介質已經鎖定

B2h 介質是可移動的

B3h 介質正在被使用

B4h 鎖定記數溢出

B5h 合法的彈出請求失敗

2. API 子集介紹
1.x 版的擴展 Int13H 調用中規定了兩個主要的 API 子集.

第一個子集提供了訪問大硬碟所必須的功能, 包括 檢查擴展 In13H
是否存在( 41h ), 擴展讀( 42h ), 擴展寫( 43h ), 校驗扇區( 44h ),
擴展定位( 47h ) 和 取得驅動器參數( 48h ).
第二個子集提供了對軟體控制驅動器鎖定和彈出的支持, 包括 檢查擴展
Int13H 是否存在( 41h ), 鎖定/解鎖驅動器( 45h ), 彈出驅動器( 46h ),
取得驅動器參數( 48h ), 取得擴展驅動器改變狀態( 49h ), int 15h.
如果使用了調用規範中不支持的功能, BIOS 將返回錯誤碼 ah = 01h,
CF = 1.

3. API 詳解

1) 檢驗擴展功能是否存在
入口:
AH = 41h
BX = 55AAh
DL = 驅動器號

返回:
CF = 0
AH = 擴展功能的主版本號
AL = 內部使用
BX = AA55h
CX = API 子集支持位圖
CF = 1
AH = 錯誤碼 01h, 無效命令

這個調用檢驗對特定的驅動器是否存在擴展功能. 如果進位標誌置 1
則此驅動器不支持擴展功能. 如果進位標誌為 0, 同時 BX = AA55h, 則
存在擴展功能. 此時 CX 的 0 位表示是否支持第一個子集, 1位表示是否
支持第二個子集.
對於 1.x 版的擴展 Int13H 來說, 主版本號 AH = 1. AL 是副版本號,
但這僅限於 BIOS 內部使用, 任何軟體不得檢查 AL 的值.

2) 擴展讀
入口:
AH = 42h
DL = 驅動器號
DS:DI = 磁碟地址數據包(Disk Address Packet)

返回:
CF = 0, AH = 0 成功
CF = 1, AH = 錯誤碼

這個調用將磁碟上的數據讀入內存. 如果出現錯誤, DAP 的 BlockCount
項中則記錄了出錯前實際讀取的數據塊個數.

3) 擴展寫
入口:
AH = 43h
AL
0 位 = 0 關閉寫校驗
1 打開寫校驗
1 - 7 位保留, 置 0
DL = 驅動器號
DS:DI = 磁碟地址數據包(DAP)
返回:
CF = 0, AH = 0 成功
CF = 1, AH = 錯誤碼

這個調用將內存中的數據寫入磁碟. 如果打開了寫校驗選項, 但 BIOS
不支持, 則會返回錯誤碼 AH = 01h, CF = 1. 功能 48h 可以檢測BIOS是否
支持寫校驗.
如果出現錯誤, DAP 的 BlockCount 項中則記錄了出錯前實際寫入的數
據塊個數.

4) 校驗扇區
入口:
AH = 44h
DL = 驅動器號
DS:DI = 磁碟地址數據包(Disk Address Packet)

返回:
CF = 0, AH = 0 成功
CF = 1, AH = 錯誤碼

這個調用校驗磁碟數據, 但並不將數據讀入內存.如果出現錯誤, DAP 的
BlockCount 項中則記錄了出錯前實際校驗的數據塊個數.


5) 鎖定/解鎖驅動器
入口:
AH = 45h
AL
= 0 鎖定驅動器
= 1 驅動器解鎖
= 02 返回鎖定/解鎖狀態
= 03h-FFh - 保留
DL = 驅動器號

返回:
CF = 0, AH = 0 成功
CF = 1, AH = 錯誤碼

這個調用用來縮定指定驅動器中的介質.
所有標號大於等於 0x80 的可移動驅動器必須支持這個功能. 如果
在支持可移動驅動器控制功能子集的固定驅動器上使用這個功能調用, 將
會成功返回.
驅動器必須支持最大255次鎖定, 在所有鎖定被解鎖之前, 不能在物理上
將驅動器解鎖. 解鎖一個未鎖定的驅動器,將返回錯誤碼 AH= B0h. 如果鎖定一
個已鎖定了255次的驅動器, 將返回錯誤碼 AH = B4h.
鎖定一個沒有介質的驅動器是合法的.

6) 彈出可移動驅動器中的介質
入口:
AH = 46h
AL = 0 保留
DL = 驅動器號

返回:
CF = 0, AH = 0 成功
CF = 1, AH = 錯誤碼

這個調用用來彈出指定的可移動驅動器中的介質.
所有標號大於等於 0x80 的可移動驅動器必須支持這個功能. 如果
在支持可移動驅動器控制功能子集的固定驅動器上使用這個功能調用, 將
會返回錯誤碼 AH = B2h (介質不可移動). 如果試圖彈出一個被鎖定的介質
將返回錯誤碼 AH = B1h (介質被鎖定).
如果試圖彈出一個沒有介質的驅動器, 則返回錯誤碼 Ah = 31h (驅動器
中沒有介質).
如果試圖彈出一個未鎖定的可移動驅動器中的介質, Int13h會調用 Int15h
(AH = 52h) 來檢查彈出請求能否執行. 如果彈出請求被拒絕則返回錯誤碼(同
Int15h). 如果彈出請求被接受,但出現了其他錯誤, 則返回錯誤碼 AH = B5h.

7) 擴展定位
入口:
AH = 47h
DL = 驅動器號
DS:DI = 磁碟地址數據包(Disk Address Packet)

返回:
CF = 0, AH = 0 成功
CF = 1, AH = 錯誤碼

這個調用將磁頭定位到指定扇區.

8) 取得驅動器參數
入口:
AH = 48h
DL = 驅動器號
DS:DI = 返回數據緩衝區地址

返回:
CF = 0, AH = 0 成功
DS:DI 驅動器參數數據包地址, (參見前面的文章)
CF = 1, AH = 錯誤碼

這個調用返回指定驅動器的參數.

9) 取得擴展驅動器介質更換檢測線狀態
入口:
AH = 49h
DL = 驅動器號

返回:
CF = 0, AH = 0 介質未更換
CF = 1, AH = 06h 介質可能已更換

這個調用返回指定驅動器的介質更換狀態.
這個調用與 Int13h AH = 16h 子功能調用相同, 只是允許任何驅動器
標號. 如果對一台支持可移動介質功能子集的固定驅動器使用此功能,則永遠
返回 CF = 0, AH = 0.
簡單地將可移動介質鎖定再解鎖就可以激活檢測線, 而無須真正更換介質.

返回:
CF = 0, AH = 0 介質未更換
CF = 1, AH = 06h 介質可能已更換

這個調用返回指定驅動器的介質更換狀態.
這個調用與 Int13h AH = 16h 子功能調用相同, 只是允許任何驅動器
標號. 如果對一台支持可移動介質功能子集的固定驅動器使用此功能,則永遠
返回 CF = 0, AH = 0.
簡單地將可移動介質鎖定再解鎖就可以激活檢測線, 而無須真正更換介質.

10) Int 15h 可移動介質彈出支持
入口:
AH = 52h
DL = 驅動器號
返回:
CF = 0, AH = 0 彈出請求可能可以執行
CF = 1, AH = 錯誤碼 B1h 或 B3h 彈出請求不能執行

這個調用是由 Int13h AH=46h 彈出介質功能調用內部使用的.

====================================================================
下面再說一下 linux 的啟動過程。
1,前面說到,MBR 會加載 OS LOADER 到 0x07C0:0000 然後開始運行,
linux 的 OS LOADER 代碼名字叫 bootsect.s,是一個長度為 512 位元組的小程式。前面也提到,多個作業系統的切換通過激活不同的分區表記錄來完成,DOS/LINUX 都有個 fdisk 程式用來做這件事,windows 的磁碟管理器可以完成類似功能,但是活動分區每次設置完之後得重啟才能生效,因此多個作業系統之間的選擇似乎不是那麼方便,因此就有了多作業系統引導程式,如 grub lilo 等等。這些程式在 MBR 的眼中看來,就是引導記錄,你甚至可以將它們直接寫到 MBR 中,覆蓋掉原有的 MBR 程式,但是我建議不要這麼做,因為 MBR 只有一個,多個作業系統之間爭奪起來會比較討厭,經常會發生你剛寫了我又寫一遍的情形,因此建議統統裝到 BOOT SECTOR,並且在硬碟分區表中將含有最強功能 OS LOADER 的那個分區激活。這樣 MBR 會將控制權交給 OS LOADER。

2,OS LOADER 接受用戶的選擇,然後啟動需要啟動的作業系統。linux 系統則開始運行 bootsect.s,

3,bootsect.s 什麼都不做,因為它只是一個 OS LOADER,它首先將自己移動到 0x9000:0000 處,然後將另一個組件 setup.s 加載到 0x9000:0200,然後 JMP 0x9000:0200 開始運行 setup.s

4,setup.s 加載作業系統的頭部 head.s 及緊隨其後的 linux 內核 main.c 移動到 0x1000:0000,然後調用 BIOS 功能獲取並保存一大堆系統參數,如磁碟參數表等等,準備接下來準備覆蓋掉 BIOS 向量表,覆蓋後所有的 BIOS 中斷都不再可用。

5,設置完畢之後,setup.s 將以 head.s 為首的 Linux 內核鏡像移動到物理地址 0x0000:0000,然後設置段表、頁表,最後調整機器狀態字到 32 位保護模式。

6,JMP 0x0 跳轉到線性地址 0x0,開始執行 head.s。
head.s 設定一些參數之後,調用 C 語言程式入口 main() 函數,作業系統進入 C 程式控制。
=====================================================================
http://www.ascc.sinica.edu.tw/nl/86/1318/05.txt

《電腦病毒與防治專題》

從硬碟架構看病毒所在

作者:譚安成

何謂病毒?病毒的分類、傳播、感染途徑、發病症狀等已在其它
文章發表過,故不在此篇文章討論範圍之內。本文將使各位讀者了
解電腦"開機的過程"、"硬碟的Partition Table"及"DBR(DOS啟動記
錄)"的結構內容,此時各位一定有所疑惑?這些又跟病毒有什麼關
係呢?想想是否曾經聽過周遭的朋友電腦,因中毒而使得硬碟無法
正常開機、硬碟資料亂掉的情形呢?這些情形大多是由於病毒破壞
電腦硬碟的Partition Table(以下簡稱 PT)、DBR、FAT 表或ROOT
(DIR)所導致的結果,然而只要PT 或DBR 遭到損壞將會使你硬碟內
寶貴且重要的資料因無法開機而無法讀取。這也是撰寫病毒之程式
設計師所喜愛下毒的兩個區域,因此如何事先備份PT、DBR 以備中
毒後之復原,將是本篇所研討的重點,除此之外也會介紹FAT 及
ROOT (DIR)架構。

開機的程序

電腦是如何開機的呢?在此我們以DOS 作業系統為例。當我們打
開電腦主機電源時主機板上的BIOS就開始測試電腦硬體及周邊設備
,此時螢幕左上角處將會看到RAM 數字從 0開始累加直至主機板上
DRAM的數目為止,之後你會聽到硬碟測試的聲音,且硬碟指示燈亮
一下表示硬碟測試沒有問題,接下來依序載入MBR(主開機程式)、
DBR(DOS啟動記錄)、IO.SYS、MSDOS.SYS、CONFIG.SYS、
COMMAND.COM、AUTOEXEC.BAT等,待螢幕出現C:>或A:>時即表示開機
成功。以圖式如下。
┌────┐   ┌────┐   ┌────┐
│POWER ON├→ │BIOS測試├→ │載入 MBR├→
└────┘ └────┘ └────┘
┌────┐ ┌────┐ ┌─────┐ ┌───────┐
│載入 DBR├→ │IO.SYS ├→ │MSDOS.SYS ├→│載入CONFIG.SYS├→
└────┘ └────┘ └─────┘ └───────┘
┌────────┐ ┌────────┐ ┌─────┐
│載入AUTOEXEC.BAT├→│螢幕出現C:>或A:>├→ │ 開機成功 │
└────────┘ └────────┘ └─────┘


硬碟的邏輯結構

硬碟的邏輯結構有如倉庫之擺設,為了有效管理倉庫我們將它依
儲存類別畫分區塊,而各區塊間又有其先後順序,就以DOS 作業系
統為例,其將整顆硬碟依序畫分為MBR、DBR、FAT、ROOT (DIR)、
DATA。
       HDD 的邏輯結構
┌───────┐
│ MBR │┐
├───────┤├ 開機時的重要程式及資料
│ DBR │┘
├───────┤
│ FAT │┐
├───────┤├ DOS 檔案組織的兩大架構
│ ROOT │┘
├───────┤
│ DATA │─ 使用者儲存在硬碟中的檔案、資料
└───────┘


DOS檔案組織的兩大架構

DOS作業系統在使用者下達DIR指令時是如何在螢幕上顯示其檔案
的階層架構呢?其實DOS 在硬碟中儲存了兩份重要的資訊即FAT和
DIR,它們用來記錄檔案位於硬碟之位址/所在,因此FAT 及DIR將
是DOS檔案組織的兩大架構,缺一不可。

何謂 FAT(File Allocation Table) 表

FAT 就是大家所稱的檔案配置表,其位於DBR 之後,而FAT 表中
記錄著硬碟內所有空間的位置,因此當我們欲建立一檔案時,DOS
作業系統將會到FAT 表處尋找何處有空位,如有空位則FAT 表將會
把此空位標成已使用,所以此動作就有如戲院的售票員,當有觀眾
買票入席時則將手上座位表上之空白座位標(上)打勾,簡言之,
FAT 表就是在硬碟中的某一區域,此區域都是記錄硬碟中那些"磁簇
(Clusters)"已被使用而那些磁簇還未被使用。

何謂 ROOT (DIR=Directory)

ROOT又有人稱做DIR,而此DIR並非DOS指令的DIR,而是我們所說
的根目錄區的意思,即然是根目錄區,那就是用來儲存DOS作業系統
根目錄的檔案及目錄,而 ROOT 配合檔案記錄表(FAT)來記錄這些檔
案及目錄,每個檔案或目錄用32 Bytes 表示,ROOT區中一共可記錄
512 個目錄或檔案。

何謂 MBR (Master Boot Record:主開機記錄)

每當我們拿到一顆新硬碟時,第一步驟就是將硬碟做分割
(Partition),並設定那一分割區為可開機磁區,這些設定值連同開
機程式將在執行分割程式後,由分割程式(如:FDISK)寫入MBR ,因
此MBR 是硬碟最重要的開機磁區,這個磁區位於硬碟的磁柱 0,磁
頭 0,磁區1 的位置也是大家俗稱的第0 軌,然而我們再將MBR分成
兩個部份;分別為MBP(主開機程式:Master Boot Program)及
Partition Table(硬碟分割表)。簡言之MBR 就是硬碟的某一區域其
大小為512 Bytes(000 - 1FF),存放著"開機程式"及"硬碟分割表"
因此一旦此區域招受破壞肯定將會無法正常開機。
    000┌───┐    ┐   000┌────────┐     ┐
│ M │ │ │ M B P │ │
│ │ │ │ │ ├ 218
│ │ │ │ │ │BYTES
│ │ │ │ │ │
│ B │ ├ 512 ─→ ├────────┤0D9 ┘
│ │ │BYTES │ 保 留 │ ┐ 228
│ │ │ ─→ ├────────┤1BE ┘BYTES
│ R │ │ │ Partition │ ┐
│ │ │ │ Table ┌──┤ │
│ │ │ │ │55AA│ ├ 66
└───┘1FF ┘ └─────┴──┘1FF ┘BYTES

MBR 的細分

下以DEBUG程式來說明MBR的內容:

1.首先以手動的方式來觀看MBR 的內容,因此就得介紹各位如何
撰寫一段小小的組合語言程式。
STEP1:在DOS 環境下執行DEBUG
STEP2:在DEBUG 環境的提示符號(-)下輸入A,即表示欲開始撰
寫組合語言程式依當時ES暫存器值而定(因此會與各位操
作時不同)

15C1:0100 mov ax,201 ┐
15C1:0103 mov bx,200 │
15C1:0106 mov cx,1 ├ 呼叫BIOS INT 13中斷服務程式讀出硬碟(DL =
15C1:0109 mov dx,80 │ 80)第0 軌(CH = 0)、第0 磁頭(DH = 0)、第1
15C1:010C int 13 │ 磁區(CL = 1)的MBR 資料放至記憶體ES:BX(
15C1:010E int 20 ┘ 15C1:200)
15C1:0110


STEP3:在DEBUG 環境的提示符號(-)下輸入G,則程式從15C1:0100
執行至15C1:010E的INT 20 結束
STEP4:在DEBUG 環境的提示符號(-)下輸入D 200,即看到15C1:200
內容如果的15C1:0280到15C1:02D0,看到"Invalid partition
tale.Error loading...."等字串,則表示主開機程式沒
有被病毒侵入。再比照前面15C1:03CE - 15C1:03FD為00
即表示此顆硬碟只切割了一個分割表。

2.接下來說明如何自行儲存PT,這時你一定會有所疑問為何不儲
存MBP呢?因為當您在使用FDISK時只要附加參數/MBR,則FDISK
將只會自動重寫一份主開機程式(MBP ) 而不會重建PT。
Step1:在DOS 環境下執行DEBUG
Step2:在DEBUG 環境的提示符號(-)下輸入A,即表示欲開始撰寫
組合語言程式

     15C1:0100 mov ax,12c   ┐
15C1:0103 mov cx,0 ├ 呼叫DOS INT 21的3C中斷服務程式開啟一空檔
15C1:0106 mov ah,3c │ PT.DAT
15C1:0108 int 21 ┘
15C1:010A push ax
15C1:010B mov ax,201 ┐ 呼叫BIOS INT 13中斷服務程式讀出硬碟(DL =
15C1:010E mov bx,200 ├ 80)第0 軌(CH = 0)、第0 磁頭(DH = 0)、第1
15C1:0111 mov cx,1 │ 磁區(CL = 1)的MBR 資料放至記憶體ES:BX(
15C1:0114 mov dx,80 │ 15C1:200)
15C1:0117 int 13 ┘
15C1:0119 pop ax
15C1:011A mov bx,ax ┐呼叫DOS INT 21的40 (AH)中斷服務程式從記憶
15C1:011C mov dx,3be ├體15C1:03BE(DX)開始寫64 Bytes(CX)資料存入
15C1:011F mov ah,40 │PT.DAT
15C1:0121 mov cx,40 │
15C1:0124 int 21 ┘
15C1:0126 mov ah,3e ┐呼叫DOS INT 21的3E (AH)中斷服務程式關閉己
15C1:0128 int 21 ┘開的檔案
15C1:012A int 20
15C1:012C db 'a:\pt.dat$' → 定義一檔名為PT.DAT
15C1:0136
-n mbr.com → 將上述程式定義一檔名MBR.COM
-rcx
-:0136 → 定義MBR.COM檔案大小為311Bytes
-w → 將MBR.COM存成一可執行檔
-q
C:>

Step3:請將一磁片放入A槽後在C:>下執行mbr.com,當程式執行
後會在A槽的根目錄下產生一大小為64Bytes的Partition
Table備份檔pt.dat。

何謂 DBR (Dos Boot Record:DOS 啟動記錄)

DBR就是存放DOS的啟動程式,主要功能就是用來載入DOS的兩個隱
藏檔(IO.SYS、 MSDOS.SYS。

同樣的底下以DEBUG 程式來說明DBR 的內容

1.首先以手動的方式來觀看DBR 的內容。
Step1:在DOS 環境下執行DEBUG
Step2:在DEBUG 環境的提示符號(-)下輸入A
      15C1:0100 mov ax,201   ┐
15C1:0103 mov bx,200 │
15C1:0106 mov cx,1 ├ 呼叫BIOS INT 13中斷服務程式讀出硬碟(DL =
15C1:0109 mov dx,180 │ 80)第0 軌(CH = 0)、第1 磁頭(DH = 1)、第1
15C1:010C int 13 │ 磁區(CL = 1)的DBR 資料放至記憶體ES:BX(
15C1:010E int 20 ┘ 15C1:200)
15C1:0110

Step3:在DEBUG 環境的提示符號(-)下輸入G,則程式從15C1:0100
執行至15C1:010E 的INT 20 結束
Step4:在DEBUG 環境的提示符號(-)下輸入D 200,即看到15C1:200
內容

2.同樣的我們也事先將DBR 儲存起來。
Step1:在DOS 環境下執行DEBUG
Step2:在DEBUG 環境的提示符號(-)下輸入A,即表示欲開始撰
寫組合語言程式

      15C1:0100 mov ax,12c   ┐
15C1:0103 mov cx,0 ├ 呼叫DOS INT 21的3C中斷服務程式開啟一空檔
15C1:0106 mov ah,3c │ DBR.DAT
15C1:0108 int 21 ┘
15C1:010A push ax
15C1:010B mov ax,201 ┐ 呼叫BIOS INT 13中斷服務程式讀出硬碟(DL =
15C1:010E mov bx,200 ├ 80)第0 軌(CH = 0)、第1 磁頭(DH = 1)、第1
15C1:0111 mov cx,1 │ 磁區(CL = 1)的DBR 資料放至記憶體ES:BX(
15C1:0114 mov dx,180 │ 15C1:200)
15C1:0117 int 13 ┘
15C1:0119 pop ax
15C1:011A mov bx,ax ┐呼叫DOS INT 21的40 (AH)中斷服務程式從記憶
15C1:011C mov dx,200 ├體15C1:0200(DX)開始寫512Bytes(AH)資料存入
15C1:011F mov ah,40 │DBR.DAT
15C1:0121 mov cx,200 │
15C1:0124 int 21 ┘
15C1:0126 mov ah,3e ┐呼叫DOS INT 21的3E (AH)中斷服務程式關閉己
15C1:0128 int 21 ┘開的檔案
15C1:012A int 20
15C1:012C db 'a:\dbr.dat$' → 定義一檔名為DBR.DAT
15C1:0136
-n dbr.com → 將上述程式定義一檔名DBR.COM
-rcx
-:0136 → 定義DBR.COM檔案大小為311Bytes
-w → 將DBR.COM存成一可執行檔
-q
C:>

Step3:請將一磁片於入A槽後在C:>下執行dbr.com,當程式執行
後會在A槽的根目錄下產生dbr.dat。

如何救復已中毒的MBR、DBR

我們將如何把未中毒前備份的PT.DAT、DBR.DAT 復原呢?另外需
告知讀者的是因為上文所備份的DBR.DAT只針對單一分割區為準,因
此做完下述步驟只能救回主分割區的資料,以下所做的步驟需特別
小心,否則一不注意將有自己破壞硬碟之虞。

Step1:使用原版DOS 磁片由軟碟開機
Step2:在A:>下執行FDISK/MBR,此時FDISK會重新寫一份新的MBP
至硬碟內。
Step3:執行DEBUG.EXE
Step4:-a
 15C1:0100 mov ax,201  ┐
15C1:0103 mov bx,200 │
15C1:0106 mov cx,1 │
15C1:0109 mov dx,80 ├ 讀取硬碟MBR(MBP+Partition Table)
15C1:010C int 13 │ 資料進入記憶體且從ES:BX(15C1:0200)
15C1:010E int 20 │ 擺起
15C1:0110 ┘
-g
Program terminated normally
-q

Step5:做完上述步驟已完全恢復硬碟的MBR
Step6:接下來恢復DBR
Step7:在A:>下執行DEBUG.EXE
Step8:
 -n dbr.dat ┐將事先備份好的資料載入記憶體,且從
15C1:0200放起 -l 200 ┘
-a
15C1:0110 mov ax,301 ┐
15C1:0113 mov bx,200 │
15C1:0116 mov cx,1 │
15C1:0119 mov dx,80 ├ 呼叫BIOS INT 13,AH = 03的中
15C1:011C int 13 │ 斷服務程式,將15C1:0200起之
15C1:011E int 20 │ 資料寫1(AL = 1) 磁區入硬碟
15C1:0110 ┘ (DL =80),第0 磁頭(DH = 0),
第0 磁軌(CH = 0),第1 磁區(CL=1)
-g

Program terminated normally
-q


Step9:救復DBR 後請重新由A 開機,在出現A:>後打入SYS C:
即可殺掉開機型病毒常隱藏的區域

十、結語

了解硬碟的邏輯架構將有助於讀者在自行PC中了開機型病毒後如
何自行救護而不借助掃毒程式,希望此篇文章能讓各位更清楚DOS
環境下硬碟的整體架構,倘若你對組合語言還不是很熟悉,想信"二
、硬碟的邏輯結構"能讓你清楚了解。

================================================================
ARM開機程序(ARM處理器核心是一個32位元的精簡指令集架構 RISC,Reduced Instruction Set Computer)

電源開啟
初始化硬體
映像檔複製到RAM
從RAM啟動映像檔
從Flash直接啟動映像檔

X86開機程序

電源開啟
BIOS初始化
POST&硬體組態檔(DRAM Controller, host bridge, PCI enumeration)
載入MBR
載入啟動區段
啟動DOS
執行Autoexec.bat

===================================================================
移植Linux至ARM嵌入式處理器
http://speed.cis.nctu.edu.tw/~ydlin/miscpub/hands-on_port-linux-to-ARM.pdf
開機程式與系統初始化
想要在一台機器上啟動一個作業系統,首先就是要能夠將處理器初始化,接著將核心載入到某個固定的起始位址上,
這個位置通常指的是SDRAM的起始點,會因處理器的不同而有所區分,也就是說,上述這些行為是具有平台相依性問題的,
而這些動作是由開機程式(bootstrap loader)來進行。接著就是將核心的本文(text)位置放到正確的位址,
如Linux 2.4.x都是將核心載入到執行位址的起始位置加上0x1000 bytes的地方,如圖8所示,此為一段核心原始碼,
裡面就註明了我們所使用的平台FootBridge,在系統開始時,需要把核心解壓縮到0x7c000000位址。
這些設定在進行核心的編譯前就需要完成,如此才能夠正確的啟動核心。
===================================================================
參考文章
(1)Migrating from x86 to PowerPC, Part 2: Anatomy of the Linux boot process
http://www-128.ibm.com/developerworks/library/pa-migrate2/?ca=dgr-lnxw01BootProcess
(2)Inside the Linux boot process
http://www-128.ibm.com/developerworks/linux/library/l-linuxboot/?ca=dgr-lnxw09LinuxBoot
(3)All the Details of many versions of both MBR and OS Boot Records
http://mirror.href.com/thestarman/asm/mbr/MBR_in_detail.htm

變數的資料型態種類

C語言變數的資料型態種類
類別符號位元位元長表示法數值範圍
整數16int(short)-32768->32767
32long-2147483648->2147483647
16unsigned int0->65535
16unsigned short0->65535
32 unsigned long0->4294967295
浮點數32float10^-38->10^38
64double10^-308-->10^308
字元8char0->255

2007年4月4日 星期三

深入淺出 Hello World Part

一個 hello world 是很多人寫程序的起點,那麼關於這個hello world 你又究竟理解有多深呢?
你敢說自己很熟悉這個簡單的不起眼的 hello world 程序背後的方方面面嗎?
看看來自台灣的 Jserv 展示的關於 hello world 的你未曾想過的不一樣的世界!

「深入淺出 Hello World Part I/II (台北場次)」簡報上線
http://blog.linux.org.tw/~jserv/archives/001844.html

「深入淺出 Hello World Part I/II (台北場次)」錄影
ftp://tnlug.linux.org.tw/video/TnLUG/2007-07-22_HelloWorld

快速修復資料庫的方法

快速修復資料庫的方法!
有的時候因為掉電或者其他原因導致資料庫損壞,我們可以使用mysql自帶的mysqlcheck命令來快速修復所有的資料庫或者特定的資料庫;例如
檢查優化並修復所有的資料庫用:

# mysqlcheck -A -o -r -p
Enter password:
database1 OK
database2 OK
----------
修復指定的資料庫用
# mysqlcheck -A -o -r Database_NAME -p
即可
另外如果只是對某個表進行修復可以用:myisamchk或isamchk
其中myisamchk適用於MYISAM類型的資料表,而isamchk適用於ISAM類型的資料表。這兩條命令的主要參數相同,一般新的系統都使用MYISAM作為缺省的資料表類型,這裏以myisamchk為例子進行說明。當發現某個資料表出現問題時可以使用:
myisamchk tablename.MYI
進行檢測,如果需要修復的話,可以使用:
myisamchk -of tablename.MYI
關於myisamchk的詳細參數說明,可以參見它的使用幫助。需要注意的時在進行修改時必須確保MySQL伺服器沒有訪問這個資料表,保險的情況下是最好在進行檢測時把MySQL伺服器Shutdown掉。
另外可以把下面的命令放在你的rc.local裏面啟動MySQL伺服器前:
[ -x /tmp/mysql.sock ] && /pathtochk/myisamchk -of /DATA_DIR/*/*.MYI
其中的/tmp/mysql.sock是MySQL監聽的Sock檔位置,對於使用RPM安裝的用戶應該是/var/lib/mysql/mysql.sock,對於使用源碼安裝則是/tmp/mysql.sock可以根據自己的實際情況進行變更,而pathtochk則是myisamchk所在的位置,DATA_DIR是你的MySQL資料庫存放的位置。

最佳化資料表
OPTIMIZE TABLE `表名`,`表名`,.....;

當我們按照原來的方式通過PHP存取MySQL資料庫時,就算設置了表的默認字元集為utf8並且通過UTF-8編碼發送查詢,你會發現存入資料庫的仍然是亂碼。問題就出在這個connection連接層上。解決方法是在發送查詢前執行一下下面這句:
SET NAMES ‘utf8’;
不過最好的方法還是在 my.cnf 中的 [client] 和 [mysqld] 章節中都加上一行 default-character-set=utf8
就可以了:
[client]
default-character-set=utf8
[mysqld]
default-character-set=utf8

C語言巨集定義技巧

轉載自:
http://miaozl.spaces.live.com/?_c11_blogpart_blogpart=blogview&_c=blogpart&_c02_owner=1&partqs=amonth%3d2%26ayear%3d2007
C語言巨集定義技巧
1,防止一個頭檔被重複包含

#ifndef COMDEF_H

#define COMDEF_H

//頭檔內容

#endif

2,重新定義一些類型,防止由於各種平臺和編譯器的不同,而產生的類型位元組數差異,方便移植。

typedef unsigned char boolean; /* Boolean value type. */

typedef unsigned long int uint32; /* Unsigned 32 bit value */

typedef unsigned short uint16; /* Unsigned 16 bit value */

typedef unsigned char uint8; /* Unsigned 8 bit value */



typedef signed long int int32; /* Signed 32 bit value */

typedef signed short int16; /* Signed 16 bit value */

typedef signed char int8; /* Signed 8 bit value */
//下面的不建議使用

typedef unsigned char byte; /* Unsigned 8 bit value type. */

typedef unsigned short word; /* Unsinged 16 bit value type. */

typedef unsigned long dword; /* Unsigned 32 bit value type. */

typedef unsigned char uint1; /* Unsigned 8 bit value type. */

typedef unsigned short uint2; /* Unsigned 16 bit value type. */

typedef unsigned long uint4; /* Unsigned 32 bit value type. */

typedef signed char int1; /* Signed 8 bit value type. */

typedef signed short int2; /* Signed 16 bit value type. */

typedef long int int4; /* Signed 32 bit value type. */

typedef signed long sint31; /* Signed 32 bit value */

typedef signed short sint15; /* Signed 16 bit value */

typedef signed char sint7; /* Signed 8 bit value */



3,得到指定位址上的一個位元組或字

#define MEM_B( x ) ( *( (byte *) (x) ) )

#define MEM_W( x ) ( *( (word *) (x) ) )

4,求最大值和最小值

#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )

#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )

5,得到一個field在結構體(struct)中的偏移量

#define FPOS( type, field ) \

/*lint -e545 */ ( (dword) &(( type *) 0)-> field ) /*lint +e545 */

6,得到一個結構體中field所佔用的位元組數

#define FSIZ( type, field ) sizeof( ((type *) 0)->field )

7,按照LSB格式把兩個位元組轉化為一個Word

#define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )

8,按照LSB格式把一個Word轉化為兩個位元組

#define FLOPW( ray, val ) \

(ray)[0] = ((val) / 256); \

(ray)[1] = ((val) & 0xFF)

9,得到一個變數的位址(word寬度)

#define B_PTR( var ) ( (byte *) (void *) &(var) )

#define W_PTR( var ) ( (word *) (void *) &(var) )

10,得到一個字的高位和低位元位元組

#define WORD_LO(xxx) ((byte) ((word)(xxx) & 255))

#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))

11,返回一個比X大的最接近的8的倍數

#define RND8( x ) ((((x) + 7) / 8 ) * 8 )

12,將一個字母轉換為大寫

#define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )

13,判斷字元是不是10進值的數字

#define DECCHK( c ) ((c) >= '0' && (c) <= '9')

14,判斷字元是不是16進值的數字

#define HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\

((c) >= 'A' && (c) <= 'F') ||\

((c) >= 'a' && (c) <= 'f') )

15,防止溢出的一個方法

#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))

16,返回陣列元素的個數

#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )

17,返回一個無符號數n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)

#define MOD_BY_POWER_OF_TWO( val, mod_by ) \

( (dword)(val) & (dword)((mod_by)-1) )

18,對於IO空間映射在存儲空間的結構,輸入輸出處理

#define inp(port) (*((volatile byte *) (port)))

#define inpw(port) (*((volatile word *) (port)))

#define inpdw(port) (*((volatile dword *)(port)))

#define outp(port, val) (*((volatile byte *) (port)) = ((byte) (val)))

#define outpw(port, val) (*((volatile word *) (port)) = ((word) (val)))

#define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))

19,使用一些宏跟蹤調試

A N S I標準說明了五個預定義的宏名。它們是:

_ L I N E _

_ F I L E _

_ D A T E _

_ T I M E _

_ S T D C _

如果編譯不是標準的,則可能僅支援以上宏名中的幾個,或根本不支持。記住編譯程序

也許還提供其他預定義的宏名。

_ L I N E _及_ F I L E _巨集指令在有關# l i n e的部分中已討論,這裏討論其餘的宏名。

_ D AT E _巨集指令含有形式為月/日/年的串,表示原始檔案被翻譯到代碼時的日期。

源代碼翻譯到目標代碼的時間作為串包含在_ T I M E _中。串形式為時:分:秒。

如果實現是標準的,則宏_ S T D C _含有十進位常量1。如果它含有任何其他數,則實現是

非標準的。

可以定義宏,例如:

當定義了_DEBUG,輸出資料資訊和所在檔所在行

#ifdef _DEBUG

#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)

#else

#define DEBUGMSG(msg,date)

#endif



20,巨集定義防止使用是錯誤

用小括弧包含。

例如:#define ADD(a,b) (a+b)

用do{}while(0)語句包含多語句防止錯誤

例如:#difne DO(a,b) a+b;\

a++;

應用時:if(….)

DO(a,b); //產生錯誤

else



解決方法: #difne DO(a,b) do{a+b;\

a++;}while(0)


宏中"#"和"##"的用法
一、一般用法
我們使用#把巨集引數變為一個字串,用##把兩個巨集引數貼合在一起.
用法:
#i nclude
#i nclude
using namespace std;

#define STR(s) #s
#define CONS(a,b) int(a##e##b)

int main()
{
printf(STR(vck)); // 輸出字串"vck"
printf("%d\n", CONS(2,3)); // 2e3 輸出:2000
return 0;
}


二、當巨集引數是另一個宏的時候
需要注意的是凡巨集定義裏有用'#'或'##'的地方巨集引數是不會再展開.

1, 非'#'和'##'的情況
#define TOW (2)
#define MUL(a,b) (a*b)

printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW));
這行的宏會被展開為:
printf("%d*%d=%d\n", (2), (2), ((2)*(2)));
MUL裏的參數TOW會被展開為(2).

2, 當有'#'或'##'的時候
#define A (2)
#define STR(s) #s
#define CONS(a,b) int(a##e##b)

printf("int max: %s\n", STR(INT_MAX)); // INT_MAX #i nclude
這行會被展開為:
printf("int max: %s\n", "INT_MAX");

printf("%s\n", CONS(A, A)); // compile error
這一行則是:
printf("%s\n", int(AeA));

INT_MAX和A都不會再被展開, 然而解決這個問題的方法很簡單. 加多一層中間轉換宏.
加這層巨集的用意是把所有宏的參數在這層裏全部展開, 那麼在轉換巨集裏的那一個巨集(_STR)就能得到正確的巨集引數.

#define A (2)
#define _STR(s) #s
#define STR(s) _STR(s) // 轉換宏
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b) _CONS(a,b) // 轉換宏

printf("int max: %s\n", STR(INT_MAX)); // INT_MAX,int型的最大值,為一個變數 #i nclude
輸出為: int max: 0x7fffffff
STR(INT_MAX) --> _STR(0x7fffffff) 然後再轉換成字串;

printf("%d\n", CONS(A, A));
輸出為:200
CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))

三、'#'和'##'的一些應用特例
1、合併匿名變數名
#define ___ANONYMOUS1(type, var, line) type var##line
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示該行行號;
第一層:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);
第二層: --> ___ANONYMOUS1(static int, _anonymous, 70);
第三層: --> static int _anonymous70;
即每次只能解開當前層的宏,所以__LINE__在第二層才能被解開;

2、填充結構
#define FILL(a) {a, #a}

enum IDD{OPEN, CLOSE};
typedef struct MSG{
IDD id;
const char * msg;
}MSG;

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};
相當於:
MSG _msg[] = {{OPEN, "OPEN"},
{CLOSE, "CLOSE"}};

3、記錄檔案名
#define _GET_FILE_NAME(f) #f
#define GET_FILE_NAME(f) _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);

4、得到一個數值類型所對應的字串緩衝大小
#define _TYPE_BUF_SIZE(type) sizeof #type
#define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
--> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
--> char buf[sizeof "0x7fffffff"];
這裏相當於:
char buf[11];