歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Unix知識 >> Unix教程

應用程序從UNIX移植到Linux技巧和要點

Linux技巧
  當前UNIX上的企業級軟件大部分都是為了迎合大公司的商務需要。因而它必須支持新出現的技術,並能順應迅速發展的市場潮流,比如強大而靈活的Linux操作系統的大量使用。由於這種軟件大部分是大型的、多線程的而且是多進程的,所以將其移植到Linux面臨著挑戰。通過本文,可以獲得在把某個企業級軟件真正移植到Linux的過程中得到的清單和建議。
  
  當前商務IT行為的一個實際情形是,很多組織正在將IT轉移到Linux,因為它具備了作為系統平台的靈活性與穩定性。另一個實際情形是,捨棄現有企業級軟件的代價過於高昂。這兩種情形經常同時出現,但關鍵是要解決它們。
  
  將企業級軟件移植到Linux可能會面臨很多有趣的挑戰。每一個步驟都必須要小心--從做出設計選擇,到獲得可用的構建系統,再到最終得到要在Linux上執行的針對特定系統的代碼。
  
  本文基於我在RHEL和SLES發行版本中(在Intel和IBM eServer zSeries體系結構上運行C應用程序)所獲得的經驗,但是這些經驗同樣適用於其他發行版本。我將討論一些在Linux上運行您的應用程序的計劃和需要考慮的技術問題,包括以下內容:
  
  * 獲得可用的構建系統。
  
  * 確定可行的操作環境。
  
  * 盡量減少在多種體系結構上構建產品所投入的精力(Linux需要得到那些體系結構的支持)。
  
  * 確定特定體系結構的變化,比如互斥鎖定(mutex locking)。
  
  * 使用新的編譯器,盡可能為多種體系結構維持一個詳盡的通用代碼基(code base)。
  
  * 確定IPC機制。
  
  * 選擇合適的線程模型。
  
  * 按Linux特定的指導方針改變安裝和包裝方式。
  
  * 確定信號選項。
  
  * 選擇解析器工具,比如lex/yacc。
  
  * 做出全局化選擇。
  
  獲得可用的構建系統
  
  支持多個平台的產品通常要求指定將要運行產品的具體操作系統。這種通用代碼通常保存在源目錄結構的獨立代碼組成部分中。
  
  例如,特定於操作系統的代碼規劃可能是類似這樣:
  
  src/operating_system_specific_code_component/aix(用於AIX)。
  
  src/operating_system_specific_code_component/solaris(用於Solaris)。
  
  src/operating_system_specific_code_component/UNIX(用於其他種類的Unix)。
  
  下圖從更為“圖形化”的角度展示了特定於操作系統的代碼規劃。
  

 

               圖1 代碼組織規劃
  
  
  獲得Linux構建系統
  
  首先,您應該為特定於Linux的代碼創建一個目錄,並將來自某個平台的文件置於其中。當您為Linux引入了一個新目錄後,規劃可能類似這樣:
  
  src/operating_system_specific_code_component/linux(用於Linux)
  
  然後這將讓我們得到一個類似如下的新的代碼規劃。
  

              圖2 新的代碼組織規劃
  
  通常,應用程序的大部分代碼通用於所有種類的Unix,也可用於Linux。經驗表明,對於特定於Linux的代碼,首先選擇特定於Solaris的文件可以最小化向Linux移植特定於平台的代碼所需的精力。
  
  然後,修改makefile並引入特定於Linux的條目:
  
  對將要使用的編譯器的定義
  
  * 程序庫路徑
  
  * 線程庫路徑
  
  * 編譯器標記
  
  * 包含文件路徑
  
  * 預處理程序標記
  
  * 需要的所有其他內容
  
  源文件中的很多改動與包含文件路徑的修改有關系。例如,要使用變量errno,需要明確地包含。
  
  在不直接包含特定於體系結構的包含文件(而是包含推薦文件)的所有地方,都必須要小心。例如,就像在中所提及的:
  
  #ifndef _DLFCN_H
  # error "Never use directly; include instead."
  #endif
  
  您應該小心地使用指示符-Dlinux 或者單詞“linux”。Linux上的預處理程序將單詞“linux”翻譯為數字1。例如,如果文件中有一個/home/linux路徑,而且使用cpp來對此文件進行預處理,則輸出文件中的路徑將是/home/1。為了避免發生這種替換,預處理程序指示符可以是類似這樣的:/lib/cpp -traditional -Ulinux。
  
  通用編譯命令
  
  程序員通常所使用的編譯器是gcc。典型的編譯命令行可能類似這樣:gcc -fPIC -D_GNU_SOURCE -ansi -O2 -c -I。-fPIC幫助生成位置無關代碼,等價於Solaris上的-fPIC。-ansi等價於Solaris上的-Xa。
  
  對於共享對象,典型的鏈接時間指示符應該是gcc -fPIC -shared -o -L-l。-shared等價於Solaris上的-G。
  
  對於擁有入口點的可重定位對象,典型的指示符可能是gcc -fPIC -shared -o -e entry_point -L-l。
  
  在開始選擇最佳操作環境之前,我將先分析在其他體系結構上編譯代碼所涉及的問題。
  
  他體系結構上的編譯
  
  另一個需要考慮的重要事項是,程序員應該能夠讓代碼盡可能容易地在其他體系結構上編譯。構建系統應該為涉及的每種體系結構准備單獨的定義文件。例如,用於x86體系結構的編譯器指示符應該有-DCMP_x86標記,用於某些特定於pSeries服務器上的Linux的代碼應該有-DCMP_PSERIES指示符。對於在x86體系結構的系統上進行的編譯,具體構建定義文件中的編譯命令行類似如下:
  
  gcc -fPIC -D_GNU_SOURCE -ansi -O2 -c -I -DCMP_x86
  
  而下面的編譯命令行用於在pSeries體系結構上進行的編譯:
  
  gcc -fPIC -D_GNU_SOURCE -ansi -O2 -c -I-DCMP_PSERIES。
  
  -CMP_x86和-CMP_PSERIES都是用戶定義標記,當程序的特定於Linux的代碼將要使用特定於體系結構的代碼時都要使用它們。我的經驗是,大部分用於Linux的應用程序代碼都是與體系結構無關的,特定於體系結構的代碼出現在需要編寫匯編代碼的地方。例如,如果您要使用比較(compare)和交換(swap)指令的實現來開發鎖,那麼您將要使用特定於體系結構的代碼。
  
  代碼的安排應該使得在代碼規劃中特定於Linux的目錄內不存在特定於體系結構的子目錄。為什麼?因為Linux已經為屏蔽體系結構細節做出了很多工作,應用程序的程序員通常不應該關心應用程序將要在哪種體系結構之上去編譯。目標應該是,以最少的精力,對代碼、代碼規劃和makefile文件進行最少的修改,就可以令為特定體系結構所編寫的程序在其他體系結構上被編譯。通過避免在linux目錄中出現特定於體系結構的子目錄,可以大大簡化makefile文件。
  
  linux子目錄中的源文件中可能會有帶有預處理程序指示符的代碼形式,如下:
  
  #ifdef CMP_x86
  
  #elif CMP_PSERIES
  
  #else
  #error No code for this architecture in __FILE__
  #endif
  
  確定可行的操作環境
  
  計劃步驟的關鍵是確定應用程序要移植到Linux的哪個發行版本。您應該確保計劃移植的程度所需要的所有軟件都可用。例如,可能不能為Linux2.6發行版本發布某個中間件產品,因為在大部分典型配置中所使用的一個關鍵的第三方數據庫在那個發行版本上不能用。最初提供的產品或者應用程序可能不得不改為基於Linux2.4發行版本。
  
  應用程序交互所需要的某些軟件,也可能並不是在應用程序所面向的所有發行版本和體系結構上都可用。對所選操作環境的可行性進行仔細研究。
  
  需要考慮的另一個問題是,應用程序是32位的還是64位的,它是否要與其他也以32位或64位模式運行的第三方軟件共存。
  
  特定於體系結構的變化
  
  應用程序中特定於體系結構的代碼通常局限於少數地方。在本節我將考慮一些示例。
  
  確定字節次序(endian-ness)
  
  程序員不必擔心是為何種體系結構編寫代碼。Linux在/usr/include/endian.h中給出了確定字節次序的途徑。您可以使用下面的典型代碼片斷來確定操作環境是big-endian還是little-endian;您可以方便地設置具體的標記。
  
  /* Are we big-endian? */
  #include
  #if __BYTE_ORDER == __LITTLE_ENDIAN
  #define MY_BIG_ENDIAN
  #elif __BYTE_ORDER == __BIG_ENDIAN
  #undef MY_BIG_ENDIAN
  #endif
  
  確定棧指針
  
  可以編寫內聯程序集(inline assembly)來確定棧指針。
  
  int get_stack(void **StackPtr)
  {
  *StackPtr = 0;
  #ifdef CMP_x86
  __asm__ __volatile__ ("movl %%esp, %0": "=m" (StackPtr) );
  #else
  #error No code for this architecture in __FILE__
  #endif
  return(0);
  }
  
  實現比較與交換
  
  這裡是為Intel體系結構實現比較與交換的一個示例。
  
  bool_t My_CompareAndSwap(IN int *ptr, IN int old, IN int new)
  {
  
  #ifdef CMP_x86
  unsigned char ret;
  
  /* Note that sete sets a 'byte' not the word */
  
  __asm__ __volatile__ (
  " lock\n"
  " cmpxchgl %2,%1\n"
  " sete %0\n"
  : "=q" (ret), "=m" (*ptr)
  : "r" (new), "m" (*ptr), "a" (old)
  : "memory");
  
  return re
Copyright © Linux教程網 All Rights Reserved