當從 OS/2 移植到 Linux 時,關鍵的編程問題是什麼?
在轉換到 Linux 之前注意一下,提早發現陷阱。LANDP 小組帶領您了解 OS/2 和 Linux 之間的差別,以便您的移植工程才能更順利地進行。
本文是 LAN Distributed Platform(LANDP)for Linux 小組把 LANDP 從 OS/2 移植到 Linux 時所遇到的問題的概述。本文對其他正在把 OS/2 應用程序移植到 Linux 的小組應該是有幫助的。當決定了要把 OS/2 版本的 LANDP 移植到 Linux 時,小組已經有了從 OS/2 移植到 NT 的經驗。NT 移植包含兩個主要的途徑: 映射層;抽象層。
抽象層比映射層稍簡單一點兒。抽象層是薄薄的一層軟件抽象,它抽象了函數名、參數以及它們的返回碼,而映射層則試圖模仿 OS/2 的行為。
對於 Linux 移植,我們從開發抽象層開始。抽象層將需要許多附加功能,這是早就顯而易見的。抽象層發展成了映射層。映射層是一個共享對象,有一個恰似 OS/2 接口的接口並且試圖模仿 OS/2 的精確行為。然而,LANDP 需要的僅是 OS/2 功能的一個子集,因此,映射層不是一個完全實現。
下面幾節概述了兩個操作系統在功能上大部分不同之處,並且提供了一些處理這些差別的建議。
明顯的差別
系統調用是 OS/2 和 Linux 之間最明顯的差別。一些調用容易被映射(例如,DosOpen),而其它的調用不容易被映射(例如,DosCreateQueue)。除了系統調用之外,返回值和返回值的含義也不同。同樣,一些返回值能被精確匹配,像 File not found,但是其它的返回值需要近似匹配。
類型是另一個產生差別的地方。因為 OS/2 重新命名了 C 的類型且使用函數參數的結構,所以就產生了差別。例如,在 OS/2 上類型 UINT 被定義為無符號整數。我們必須為 Linux 環境重定義這些類型中的大多數。
操作上的差別
最主要的區別在於概念的行為,這甚至比移植系統調用和重定義類型更為重要。大多數操作系統具有相同的概念、內存、文件、進程間通信(IPC)等等。OS/2 和 NT 有非常相似的概念且它們的差別不是大范圍的(除了共享內存外)。然而,Linux 和 OS/2 顯示了許多概念上的差別。下面這些子節概述了這些差別,並且為處理這些差別提供了一些解決方案。
線程
Linux 環境中的線程是一個特殊類型的進程。因此,要為您使用的每個線程都創建一個新進程。而且,通過使用 clone 函數或一個叫 pthread 的單獨的庫來實現線程。也存在其它形式的線程,但是 pthread 是一個 POSIX 定義的標准。
因為對 Linux 而言線程是一個相對新的概念,所以這個操作系統不是和其它操作系統一樣線程安全。因此,一些標准庫並不是真正的線程安全。另外,線程會影響信號的行為。特定的線程將接收一個被發送到進程的信號,這個特定的線程是未知的。請注意,pthread 有它們自己在一個進程中的每個線程之間傳送信號的方式。
getpid 和 getppid 的使用將受到影響。例如,一個調用 getpid 的線程,它的進程標識(不是應用程序的進程標識)將被返回,因為線程被作為進程來實現。
內存
進程內存在 Linux 和其它操作系統中的使用非常類似,但在進程間共享內存的機制不同。Linux 提供了一組基於 System V IPC 機制的 API。在這組 API 中處理共享內存。共享內存機制為一個進程可以擁有的段數量以及整個系統的段數量定義了界限。遺憾的是,這個界限限制了一個應用程序能使用的共享內存段的數量。
例如,如果一個 OS/2 應用程序創建了 500 個共享內存區域,您就無法使用到 System V 共享內存 API 的直接映射。盡管您可以重新編譯內核並設置新的層次,但是必須考慮您的顧客們是否能接受這種改變。
您可以分配一個巨大的共享內存段,然後在這段內分配小一些的塊。然而,沒有邊界檢查。沒有邊界檢查,應用程序會損壞已分配給其它進程的段。
實現一個與 OS/2 (它進行一些邊界檢查)類似的行為的另一個方法是使用文件映射。文件映射將一個文件映射到一個進程的內存空間內,以便允許把文件像內存一樣來訪問。沒必要整個文件映射到內存,但可以把文件的一部分映射到一個內存區域。
共享內存機制的另一個差別是:當一個段被加載到一個進程中時,Linux 不保證會使用相同的內存地址。在 OS/2 上,您可以分配一塊內存並將這一塊內存傳送給另一個進程。內存位於每個進程中相同的地址處。在 Linux 上情況並不一定如此。另外,為了訪問另一個進程已創建的一個共享內存段,您需要知道這個段的標識。標識是一個獨一無二的數字,可以通過使用一個名稱(如果該名稱作為一個文件存在)來計算這個數字。OS/2 提供幾個系統調用,使得進程能夠給出且獲得到基於名字或內存地址的共享內存段的訪問。
隊列
OS/2 隊列是一個允許進程把內存指針傳給另一個進程的機制。傳送進程必須通過 DosGiveSharedMemory 給接受進程提供到內存段的訪問,然後把內存在段中的位置傳送給接收進程。像機制名暗示的一樣,隊列可以在等待另一個進程讀取它們的同時保存大量的這些內存位置。Linux 沒有這個概念。
在可能的解決方案中,其中之一就是使用 System V 共享內存段。System V 共享內存段的問題是限制了被允許的段的數量。
另一個方法是使用文件映射並使用控制結構,該結構設置元素(內存位置)的順序和對文件的訪問權限。
信號量(Semaphore)
OS/2 提供三種主要類型的信號量:事件、互斥和多等待。Linux 提供一個基於 System V IPC 的信號量機制並且支持信號量作為 pthread 庫的一部分。OS/2 信號量是一個單獨的實體,給您提供一個定義良好的行為。然而,可以將 System V 信號量定義成幾組,這是可配置的。在不重新編譯內核的情況下,每個組最多可以有 250 個信號量(2.4 內核)。這種系統在系統內只能有一定數量的信號量組(可以通過重編譯內核來更改這個數量)。
System V 信號量基本上是計數變量,您可以增加或減少這些變量,並且這為您模仿事件信號量和互斥信號量的行為提供了足夠的功能。唯一的問題是執行定時等待。在 Linux 上,您只能嘗試/等待或等待。不允許超時。為了模仿超時,您需要構建某種定時機制,該機制能發送一個信號來中斷正在等待一個事件的線程。
多等待信號量是一個已定義信號量的集合,這些信號量可以作為一組等待。System V 機制利用組,並且能等待許多與同一個組相關的操作。然而,只有應用程序仔細地規劃了它對信號量(多等待中的所有信號量都來自於同一個信號量組)的使用,您才能得到期望的結果。我們實現了一個機制,它使線程能夠等候這組內的每個信號量來模擬這一行為。
pthread 庫提供了能合理模仿 OS/2 行為的信號量和互斥(以及條件變量)。然而,還存在缺陷。請注意,pthread 信號量和互斥是不可共享的。換言之,僅在那個進程中保證它們。如果您的應用程序僅使用私有信號量,或者如果沒有其它進程需要訪問它的信號量,那麼使用 pthread 互斥和信號量可能是最好的計劃。但是,如果您的應用程序共享這些 IPC 機制,那麼您需要用 System V 機制、共享內存和線程來實現這些機制。
信號
在 OS/2 上,為了指明某些錯誤情況,要拋出異常。在 Linux 上,這些被稱為信號。與異常一樣,除了 SIGKILL 和 SIGSTOP 之外,信號都可以被捕獲。在 OS/2 上,您可以定義一列在接收到異常時需要執行的函數。應用程序可以在 OS/2 上定義不止一個異常處理程序。在 Linux 上,因為函數被覆蓋了,所以您只能定義一個異常處理程序。
管道
在 OS/2 上,管道是雙向的,而 Linux 上的管道是單向的。為了模仿 OS/2 管道的行為,您需要創建兩個管道,通過一個文件句柄來索引。
共享對象
Linux 共享對象和 DLL 非常相似,但是需要注意幾個陷阱。應用程序可以在一個共享對象中重寫函數。如果一個共享對象有 print_hello 函數並且應用程序有一個叫作 print_hello 的函數,那麼無論何時應用程序調用 print_hello,都使用應用程序的版本。您在共享對象中調用一個函數之前(例如,so_print 調用 print_hello),這可能聽上去不像什麼問題。這種情況中使用的 print_hello 是應用程序中定義的那個。
感覺上的標准函數
您在作關於標准 C 函數方面的假設時應該謹慎一些。例如,kbhit 和 strupr 不是標准函數。盡管它們可能是一個編譯器的 C 標准庫的一部分,但是假定這些函數和其它函數存在於所有平台上是不安全的。
結束語
前面所述的差別無論如何不包含一個確定的列表。這樣一個列表中包含的信息足夠寫一本書了。然而,當您將一個應用程序從 OS/2 移植到 Linux 上時,這些差別應該使您可以提前找到需要解決問題的地方。
LANDP for Linux 小組設計了一個映射層來幫助我們從 OS/2 向 Linux 移植。映射層是一個共享對象,用於從 OS/2 移植各個 LANDP 服務器。可能映射層將為其它項目提供一個起點,也許不會提供。
關於作者
Kevin Bowkett 是一名在 LANDP 開發組中工作的 IBM 軟件工程師,在 Linux、Windows 和 OS/2 操作系統方面有很好的基礎。在過去的一年中,Kevin 帶領 LANDP 小組將 LANDP for Linux 投入了市場。Kevin 還是一名獲 IBM 認證的 DB2 應用開發解決方案專家。如果您正在從事從 OS/2 到 Linux 的移植並且有什麼問題的話,盡可以通過 [email protected] 和他聯系。