前言 :
最近做的一些工作和平台移植有關, 大致目標是將原來運行在Unix下的一套東西(介面:Cobol, Logic:Cobol, C), 移植到Windows下面(介面:VB.NET, Logic : Cobol, C).
為解決大規模C代碼移植(希望盡量少改寫或不改寫), 先後嘗試了SFU3.5 (on WinXP) 以及Cygwin.
本文主要講解運用Cygwin進行類似代碼移植所遇到的問題以及一些常見的解決方案.
(SFU3.5將在下一篇中簡單介紹.)
簡單說明 :
要進行Unix -> Windows 的C代碼移植, 首先考慮到的是MinGW提供的GCC編譯器, 但是很遺憾, 代碼沒有能象我想象中的那麼簡單的就通過編譯了. 原因在於原Unix-C-Source中, 用到了某些特定的系統Function ,如 : shmat(...), shmget(...) etc. (作用大致和共用記憶體相關).
要想在Windows上運行這些Unix系統Function , 可能需要用到Windows下的Posix子系統來真正類比Unix的一整套環境 : 如Login , Shell命令操作, 特定系統Function等. (Posix是一個標準, 主要解決Linux與Unix間的通訊標準等, 可以理解為Linux和Unix必須支援的最小特性集. 在Windows上支援Windows子系統, Posix子系統, 可能還有OS2. Windows上的Posix子系統即是支援Posix標準, 以便和Unix互動.)
具體的關於Posix子系統的一些講解請參見:
http://archie.ncu.edu.tw/icas/refpage/2b/3b/2b3be9c51fef572b2c8d498ad2bd3713.html
http://www.xemacs.org/Documentation/21.5/html/internals_37.html
既然設計到環境, 就得考慮目前流行的兩種移植平台 : SFU 以及Cygwin. (最後實際採用的是Cygwin, 原因會在SFU的文章中介紹), 下面就具體介紹Cygwin :
Cygwin運行原理大致是 : 核心幾乎完全依賴於Windows子系統的Function, 並在相當於Windows子系統上構建一個Cygwin平台, 這個Cygwin平台提供一些特定的服務,標頭檔(和MinGW一致), Shell命令, 還有特定的Cygwin-GCC etc.. 使用Cygwin-GCC編譯的C-Source在入口的時候用由Cygwin進行一些初始化, 使編譯好的應用程式感覺就是在一個Unix平台上運行一樣.
下面介紹我使用Cygwin的全過程 :
1. Cygwin - Install :
Cygwin據稱全部安裝大約需要4GB空間, 在我進行的工程中並不需要那麼多, 到www.cygwin.com去下載一個安裝程式, 按照下面的步驟安裝:
A. 選擇Download without Installing , download除預設的一些東東, 還需要 : Admin / cygrunsrv, Devel下的GCC, GDB, MinGW等等(幾乎全部)
cygrunsrv : 包括cygserver服務, 如果Source中用到一些如共用記憶體的, 就需要這個Win-Service.
Gcc / Gdb : Cygwin下的編譯器及Debugger.
MinGW : 可能內建開發的IDE, 以及一些標頭檔. (我由於實現安裝了MinGW, 所以沒注意)
B. 下完後, 再運行Installer, 選擇Install, 注意把剛才下的全部選擇安裝.
2. Enviremnt - Setting :
安裝好後, 注意要設一些東西: (假設你安裝的目錄為C:/Cygwin, 則預設這個目錄為根目錄)
(另外注意: Cygwin映射Windows盤如: D:/MyWork --> /cygdrive/D/MyWork)
A. 添加C:/Cygwin/bin 到你的環境變數PATH中, 方便使用.
B. 運行 /usr/sbin 下面的cygserver.exe (這裡應該是安裝服務)
如果安裝好了, 可以用/bin/cygrunsrv 查看服務.
Eg.
cygrunsrv -L //列出Cygwin當前啟動並執行所有服務
cygrunsrv -E cygserver // 停止cygserver服務, 這將導致共用記憶體被清空等一系列操作.
cygrunsrv -S cygserver // 啟動cygserver 服務, 注意, 只有啟動了這個服務, 共用記憶體等操作才成為可能.
C. 添加 CYGWIN = server 到環境變數中. (MUST)
3. 到此Cygwin基本OK了,可以在CMD中運行bash --login -i 進入Cygwin-Shell, 嘗試ls, rm 等命令確保Cygwin安裝成功. (在Cygwin-Shell裡用GCC嘗試編譯一個HelloWorld, 並確保GCC安裝成功).
關於Cygwin的使用, 具體參見 : www.cygwin.com, 裡面有很多FAQ是很多初學者的天堂:)~
下面代碼測試Unix-特定Function使用 (注意cygserver要處於運行狀態)
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/shm.h>
# define DLLEXPORT __declspec (dllexport) //這裡其實是不需要的, 因為Cygwin-GCC預設匯出所有function.
DLLEXPORT void AllocMemory(int key, const char* str)
...{
int segment_id;
char* shared_memory;
struct shmid_ds shmbuffer;
int segment_size;
key_t keys;
const int shared_segment_size = 0x6400;
printf ("------------- Begin - Alloc Memory-------------- ");
keys= ftok("/usr", key);
printf ("1 - keys = %d ", keys);
/**//* Allocate a shared memory segment. */
//IPC_GET | IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR IPC_STAT
segment_id = shmget (keys, shared_segment_size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
if (segment_id == -1)
...{
printf ("Already alloc memory. ");
return;
}
printf("2 - segment-ID = %d ", segment_id);
/**//* Attach the shared memory segment. */
shared_memory = (char*) shmat (segment_id, 0, 0);
printf ("3 - shared memory at = %p ", shared_memory);
/**//* Determine the segment’s size. */
shmctl (segment_id, IPC_STAT, &shmbuffer);
segment_size = shmbuffer.shm_segsz;
printf ("4 - Get segment size: %d ", segment_size);
/**//* Write a string to the shared memory segment. */
sprintf (shared_memory, str);
/**//* Detach the shared memory segment. */
shmdt (shared_memory);
printf ("5 - share memory : %s. ", str);
printf ("------------- Over ---------------- ");
}
DLLEXPORT void ReadMemory(int key)
...{
printf ("------------- Begin - Read Memory-------------- ");
int segment_id;
char* shared_memory;
struct shmid_ds shmbuffer;
int segment_size;
key_t keys;
const int shared_segment_size = 0x6400;
keys= ftok("/usr", key);
printf ("1 - keys = %d ", keys);
//return it
segment_id = shmget (keys, shared_segment_size, IPC_CREAT);
if (segment_id == -1)
...{
printf ("Havn't alloc memory yet. ");
return;
}
printf("2 - segment-ID = %d ", segment_id);
/**//* Attach the shared memory segment. */
shared_memory = (char*) shmat (segment_id, 0, 0);
printf ("3 - shared memory at = %p ", shared_memory);
printf ("4 - shared memory content = %s ", shared_memory);
printf ("------------- Over ---------------- ");
}
用AllocMemory(...) 分配記憶體(注意相同的索引值只能分配一次, 除非cygserver-restart ^_^. ), 用ReadMemory(...)擷取剛才分配的記憶體裡的資料(注意除非cygserver-restart , 否則值是一直存在的, 不會因為運用程式退出而清除)
Cygwin-GCC 編譯的簡單說明:
gcc -shared xxx.c -o xxx.dll //編譯成DLL
gcc -c xxx.c -o xxx.o //編譯成OBJ
gcc xxx.c -o xxx.exe //編譯成EXE
ar r xxx.lib xxx1.o xxx2.o //打包多個obj成一個Lib (非串連)
gcc xxx.c ./L aaa.dll -o xxx.exe //引用外部DLL產生EXE (當然也可以自己寫Def 產生Lib, 再includingLib.)
關於GCC編譯以及MakeFile的簡介會在以後的文章中說明.
------------------ Above Over All ----------------------------------------------------------------------