標籤:style http color io os 使用 ar for 檔案
linuxpatchlinux核心文檔commandheader類unix作業系統有一個很有趣的特性就是原始碼級的補丁包。在windows上我們打補丁都是運行一個可執行檔程式,然後就可以把補丁打完了,這對於終端使用者是非常方便的,但是對我們這些求知慾比較強的Linux fans來說就有點不過癮了,因為我們不知道裡面是怎麼做的。而Linux的補丁就有趣多了,我們首先獲得程式的原始碼和對應的補丁檔案,然後給原始碼打補丁,產生新的原始碼檔案。然後再編譯這個新的原始碼檔案,就獲得了打過補丁的新程式了。 如果你現在還不懂得如何打補丁,那沒有關係,我們就一起來試試,因為在剛開始寫這篇文章的時候,我也不會給自己的程式打補丁。
確切的說,這篇文章不是我寫的,而是我根據Daniel P. Bovet, Marco Cesati, 和Cosimo Comella寫的一篇英文文檔翻譯並修改得出來的。之所以這麼說,我是不想落抄襲之嫌。
言歸正傳:
解釋patch檔案
我們可以使用diff命令加參數-ruN來比較兩個檔案並產生一個補丁檔案。這個補丁檔案會列出這兩個不同版本檔案的差異來。我們將通過一個特定例子來解釋這個由diff命令產生的補丁檔案(patch file)。
假定:我們對檢查linux-2.2.13和linux-2.2.14這兩個不同的版本的差別高度興趣。
第一步,我們使用如下命令:
make distclean
這樣可以在兩個原始碼目錄中刪去所有非文字檔。
然後我們繼續第二步:
*****************************************************************************
diff -ruN linux-2.2.13 linux-2.2.14 > /tmp/patch-2.2.14
*****************************************************************************
COMMAND EXECUTION:
-r 是一個遞迴選項,設定了這個選項,diff會將兩個不同版本原始碼目錄中的所有對應檔案全部都進行一次比較,正如你所料,這種比較也是包括子目錄中的檔案的。
-N 選項表明如果一個檔案存在於一個目錄中,那麼它就必須被認為是在這個目錄中的,哪怕這個檔案在對應的目錄中是一個空檔案。(舉個例子,如果在老版本中有這麼一個檔案,但是在新版本中這個檔案被去掉了,那麼diff仍然會把它記錄下來,我們打完補丁以後,在得到的新版本代碼中,老版本的那個檔案名稱仍然會存在,但是是一個空檔案)
-u 選項指明正在使用的是統一的輸出格式。
下面我們查看一下經過重新導向後產生的補丁檔案/tmp/patch-2.2.14,下面是我們從該檔案中摘抄的一部分補丁資訊:
*****************************************************************************
diff -ruN linux-2.2.13/arch/i386/kernel/signal.c
linux-2.2.14/arch/i386/kernel/signal.c
這裡第一個版本的名字,linux-2.2.13是參考版本(就是舊版本),所有的在linux-2.2.14(新版本)中發現的問題都是和第一個版本相關的。
*****************************************************************************
DIFF HEADER:
diff命令會在補丁檔案中記錄這兩個檔案的首次建立時間,如下:
*****************************************************************************
*** linux-2.2.13/arch/i386/kernel/signal.c Tue Jun 8 01:14:20 1999
--- linux-2.2.14/arch/i386/kernel/signal.c Sun Jan 23 17:29:25 2000
*****************************************************************************
DIFF BODY:
diff 命令在這兩個檔案之間發現了3類差異。
a) 添加(addition):
這一行在舊版本的檔案中沒有,但是被添加到了新版本的檔案中。
b) 置換(replacement):
在新版本檔案中用連續的幾行替換掉了舊版本檔案中連續的幾行。
c) 刪除(deletion):
在舊版本檔案中的一行在新版本檔案中不再出現。
在每一種情況下,發生變化的行號都會被提示出來。
讓我們解釋一下diff用來指明這三種情況時使用的符號:
*****************************************************************************
添加(ADDITION):
看補丁檔案中如下的行: (相對於arch/i386/kernel/signal.c檔案的新舊兩個版本)
*** 419,431 ****
--- 419,437 ----
? current->exec_domain->signal_invmap[sig]
: sig),
&frame->sig);
+ if (err)
+ goto give_sigsegv;
使用+號指明在“&frame->sig);”這一行後面要加兩個新行。這兩個新行就是用+符號開頭的兩行。
*** 419,431 ****向讀者指明可以從舊檔案的419行到431行來查閱這些變化;同樣的,--- 419,437 ----向讀者指明可以從新檔案的419到437行來查閱這些變化。這樣一來,新舊一比較,就可以知道哪些地方發生了什麼變化。
不過在新版本的diff中,似乎並不是用這種方法來表明新舊檔案對應的行號的,而使用@這個符號,對應於上例中的:
*** 419,431 ****
--- 419,437 ----
我們看到的新的標識可能是:
@@ -419,431 +419,437 @@
對於這種表示方法,我還不是很懂,如果有哪位朋友比較懂的話,非常歡迎你將這部分內容加進來。
不過,有一點需要說明一下,就是這個行號並不是完全必需的,其實這個行號在給原始碼打補丁的時候是沒有用的,這裡提示出來主要是給開發人員比較分析時使用的。
置換(REPLACEMENT):
看補丁檔案中如下的行: (相對於arch/i386/kernel/signal.c檔案的新舊兩個版本)
***************
*** 367,377 ****
printk("I/O APIC #%d Version %d at 0x%lX./n",
m->mpc_apicid,m->mpc_apicver,
m->mpc_apicaddr);
! /*
! * we use the first one only currently
! */
! if (ioapics == 1)
! mp_ioapic_addr = m->mpc_apicaddr;
}
mpt+=sizeof(*m);
count+=sizeof(*m);
然後後面又緊跟著如下的行:
--- 368,376 ----
printk("I/O APIC #%d Version %d at 0x%lX./n",
m->mpc_apicid,m->mpc_apicver,
m->mpc_apicaddr);
! mp_apics [mp_apic_entries] = *m;
! if (++mp_apic_entries > MAX_IO_APICS)
! --mp_apic_entries;
}
mpt+=sizeof(*m);
count+=sizeof(*m);
這裡就指明了在舊版本檔案中用!符號標識的5行被在新版本檔案中用!標識的3行替換了。
由此可見,符號!就意味著替換。但是為什麼會有替換,而不是先刪除再添加,這裡我就不清楚了。還是得請知道的朋友指點一下了。
刪除(DELETION):
看補丁檔案中如下的行: (相對於drivers/net/Config.in檔案的新舊兩個版本)
***************
*** 93,100 ****
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate ‘RealTek 8129/8139 (not 8019/8029!) support‘ CONFIG_RTL8139
- tristate ‘SiS 900 PCI Fast Ethernet Adapter support‘ CONFIG_SIS900
- tristate ‘Packet Engines Yellowfin Gigabit-NIC support‘ CONFIG_YELLOWFIN
fi
bool ‘Other ISA cards‘ CONFIG_NET_ISA
if [ "$CONFIG_NET_ISA" = "y" ]; then
--- 94,99 ----
***************
在舊版本檔案中用-符號標識的兩行說明這兩行在新版本的檔案中不會再出現了,也就是說,在新版本的檔案中,這兩行被刪除了。
建立一個自己的補丁
你現在修改並測試了一個新的Linux版本,就稱它為Linux-2.4.5kh3,這個版本和你當前使用的稱作Linux-2.4.5kh2的“老”版本有一些些不同。
現在你想製作一個可以將Linux-2.4.5kh2升級到Linux-2.4.5kh3的補丁程式。順便多說句廢話,理所當然的這個補丁程式要比Linux核心的原始碼小的多。
這個補丁檔案一般使用一張磁碟片就可以裝下來,因此這對於升級另一台電腦上的舊的作業系統核心是非常有用的。
本質上製作補丁程式只有兩個步驟,如下描述:
a) 在電腦A上產生一個補丁檔案(電腦A就是那台既有新核心的原始碼又有老核心的原始碼的電腦)。並且將這個補丁檔案複製到一張磁碟片上。
b) 在電腦B上讀取儲存有補丁檔案的磁碟片,並利用補丁檔案將電腦B上的舊核心升級為新的核心。
下面我們進行詳細的說明。其中第1到第4步描述了怎樣製作一個補丁檔案,並把它複製到磁碟片上。第5到第6步描述了怎麼樣使用補丁檔案將舊的作業系統核心升級到新的版本。
*****************************************************************************
第1步:清理兩個核心的原始碼檔案(沒有*.o的檔案或者.*檔案)
*****************************************************************************
我們假定這兩個核心的原始碼路徑分別是:
/usr/src/linux-2.4.5kh2和/usr/src/linux-2.4.5kh3
運行如下命令:
cd /usr/src/linux-2.4.5kh2
make distclean
cd /usr/src/linux-2.4.5kh3
make distclean
*****************************************************************************
第2步:在兩個核心原始碼版本之間產生一個“context diffs”檔案(這個檔案指明了兩個不同版本原始碼之間的所有不同)。
*****************************************************************************
運行如下命令(首先是舊的核心,然後是新的核心):
cd /usr/src
diff -ruN linux-2.4.5kh2 linux-2.4.5kh3 > patch-2.4.5kh3
*****************************************************************************
第3步:檢查補丁檔案。
*****************************************************************************
運行如下命令查看補丁檔案以確定它沒有包含任何的垃圾:
less patch-2.4.5kh3
這裡所說的垃圾就是非ASCI碼的亂碼,或控制字元。如果發現補丁檔案中存在著不是文本的內容,那就是有垃圾了。這是我們需要重新操作第1至第3步
*****************************************************************************
第4步:將補丁檔案複製到一張磁碟片上。
*****************************************************************************
mount /flp
cp /usr/src/patch-2.4.5kh3 /flp
umount /flp
由於我們的補丁檔案一般都很小,所以我們不需要壓縮它。現在我們拿著這張帶有補丁程式的磁碟片轉移到電腦B前面去。
*****************************************************************************
第5步:從磁碟片中讀取補丁檔案。
*****************************************************************************
cd /usr/src
mount /flp
cp /flp/patch-2.4.5kh3 patch-2.4.5kh3
umount /flp
*****************************************************************************
第6步:使用這個補丁檔案將舊核心的原始碼升級到新核心的版本。
*****************************************************************************
a) 執行奇妙的patch命令:
patch -p0 < patch-2.4.5kh3
patch命令作用在輸入的補丁檔案patch-2.4.5kh3上,並將對應的老版本的核心原始碼中所有的檔案和子目錄升級到對應的新版本(當然這裡的老版本一定要和我們在電腦A上運行diff命令時的那個老版本是一樣的)。在我們的例子中,舊的核心原始碼版本是Linux-2.4.5kh2。這裡參數–p0用來保證檔案名稱不被改變(既不被修改,也不被刪除)。
b) 重新命名核心原始碼:
mv linux-2.4.5kh2 linux-2.4.5kh3
*****************************************************************************
撤消一個補丁
*****************************************************************************
如果你對新打的補丁不很滿意,而你想回複到以前較早的核心版本去,那麼我們從上面描述的第6步開始逆順序操作即可(為什麼要採取撤消補丁的方式來回到先前版本的理由很多,我們就不說了):
a) 將核心原始碼的版本恢複到先前的名字:
mv linux-2.4.5kh3 linux-2.4.5kh2
b) 執行那條奇妙的patch命令:
patch -RE -p0 < patch-2.4.5kh3
這裡,-E選項說明如果發現了空檔案,那麼就刪除它;-R選項說明在補丁檔案中的“新”檔案和“舊”檔案現在要調換過來了(實際上就是給新版本打補丁,讓它變成老版本,但是這裡是否一定要先更改原始碼的目錄名我還不明確,需要作實驗)。
這裡有一個疑問,就是為什麼要進行代碼目錄的改名?
*****************************************************************************
避免make distclean操作
*****************************************************************************
根據文檔/usr/src/linux/Documentation/SubmittingPatches所描述的,運行一個沒有make distclean的遞迴diff,使用如下命令:
diff -ruN -X dontdiff linux-2.4.5kh2 linux-2.4.5kh3 > patch.diff
dontdiff是一個可執行檔,它可以從下面這個網址進行下載:
http://www.moses.uklinux.net/patches/dontdiff
但是,就我而言,我還是不知道為什麼要這麼做,或者說不進行make distclean的目的是什麼。如果有那位朋友知道的話,非常感謝你告訴我一聲,或者是把這部分內容補充到這份文檔中。
製作一個官方的補丁檔案
你已經安裝了一個新的Linux,我們稱它為Linux-2.4.2。但是差不多每個月,都會有新的Linux補丁推出,這些補丁或者修補了一些問題,或者支援了一些新的硬體或其它新的特性。所以,當你安裝了Linux-2.4.2幾個月以後,就會有新的穩定發行版推出,我們稱它為Linux-2.4.5。
我們將要描述的過程解釋了你如何最簡便的將自己的Linux從2.4.2升級為最新的2.4.5。
關鍵的竅門在於使用一系列的補丁檔案。
*****************************************************************************
第0步:理解官方的Linux補丁檔案是如何製作的。
*****************************************************************************
每一個官方的Linux補丁檔案都是一個使用diff工具在兩個連續的Linux版本中所有檔案間產生的diff檔案。
作為一個例子,我們假定這個補丁檔案是用來將Linux-2.4.2升級到Linux-2.4.5的,這個補丁檔案通過如下方法獲得:
cd /usr/src/linux-2.4.2
make distclean
cd /usr/src/linux-2.4.3
make distclean
ln -s linux-2.4.2 linux
diff -ruN linux linux-2.4.3 > patch_2.4.3
gzip patch_2.4.3
這些補丁檔案按照順序使用gzip壓縮,以節省Linux發行版本網站的下載時間。例如我們產生的補丁檔案——patch_2.4.3.gz,就是一個經過壓縮的補丁檔案。
Linux官方補丁總是認為預設的原始碼子樹名字為“Linux”。這種方法可能有點混淆,但是它允許一些(版本)連續的補丁不用修改原始碼子樹的名字就可以連續的被修改到原始碼子樹上。(下面我們可以看到)
這些補丁檔案的名字都是標準的:
patch_2.4.3 記錄著linux-2.4.3 和 linux-2.4.2之間的版本差異。
patch_2.4.4 記錄著linux-2.4.4 and linux-2.4.3之間的版本差異。
patch_2.4.5 記錄著linux-2.4.5 and linux-2.4.4之間的版本差異。
在我們的例子中,我們正好需要這3個補丁檔案,他們的名字是patch_2.4.3.gz、patch_2.4.4.gz和patch_2.4.5.gz,用來將Linux-2.4.2升級到Linux-2.4.5。
*****************************************************************************
第1步:升級Linux原始碼目錄的名字和Linux符號串連。
*****************************************************************************
假定核心原始碼的路徑是:/usr/src/linux-2.4.2/
並且所有需要的壓縮補丁檔案都已經存放在目錄/usr/src 中了。
執行下列命令:
cd /usr/src
mv linux-2.4.2 linux-2.4.5
rm linux
ln -s linux-2.4.5 linux
*****************************************************************************
第2步:清理原始碼目錄(刪除中間目標檔案和設定檔)。
*****************************************************************************
執行下列命令:
cd linux
make distclean
*****************************************************************************
第3步:解壓縮補丁檔案。
*****************************************************************************
in our example, execute:
cd /usr/src
gunzip patch-2.4.3.gz
gunzip patch-2.4.4.gz
gunzip patch-2.4.5.gz
*****************************************************************************
第4步:重複執行patch命令。
*****************************************************************************
執行下列命令:
for i in 3 4 5; do
patch -p0 < patch_2-4.$i
done
其中
patch -p0 < patchfile
命令作用在輸入的補丁檔案上,並將對應的老版本的核心原始碼中所有的檔案和子目錄升級到對應的新版本(在我們的例子中,老版本的核心原始碼目錄是/usr/src/linux)。在我們的例子中,舊的核心原始碼版本是Linux-2.4.5kh2。這裡參數–p0用來保證檔案名稱不被改變(既不被修改,也不被刪除)。
目標目錄是Linux,就是說舊版本的核心原始碼經過patch以後,就被升級成了新版本的核心原始碼。
Linux打補丁的一些問題