一、前言
iceE1.3.0中間件當前版本並沒有直接支持VxWorks,我們可能在百度搜索上也找不到相關移植資料。在Windows,unix,linux等相關操作系統下,也許你可以輕鬆地搞定。或許估計你曾經嘗試把它移植到VxWorks實時操作系統,其過程估計沒有想象那麼容易,只有身臨其中才能有所體驗了。
在Ice移植VxWorks過程中,我們需要定義宏“VXWORKS”開關。VxWorks底層網絡遵循POSIX標準,但不同的操作系統中POSIX還是有一點差異,代碼修改過程中使用VXWORKS標誌區別出來。
本文基於VxWorks本身的仿真器實現,完成ice的動態庫創建,並使用最簡單的例子“Hello World!”實現客服與服務器之間的通信。
讓我們開始進入正式的主題吧!
二、瞭解POISX標準
POSIX具有多重含義,通常指POSIX標準,該標準是一個可移植操作系統接口(Portable OperatingSystem Interface),由IEEE提出,ANSI和ISO將其標準化。POSIX的目的是使應用程序源代碼可以在兼容POSIX的操作系統上移植。理想的目標是應用程序移植到另一個操作系統只需要重新編譯就可以運行。POSIX最後一個字母“X”表達了這種超乎操作系統差異的理想。目前並沒有實現這種理想:從操作系統看,由於目標、要求、理念、條件的差異,並不是所有的操作系統都實現100%POSIX兼容;從應用程序看,很多代碼編寫使用了特定操作系統支持的調用,並沒有很好地使用POSIX接口。但是,很顯然,使用POSIX接口的應用程序在兼容POSIX的操作系統間移植將是很輕鬆的事情。
POSIX標準是一個處於不斷髮展之中的龐大體系,包括:
1003.1 系統API
1003.2 SHELL及工具
1003.3 POSIX符合性測試方法
1003.5 ADA語言接口
1003.13 標準化可移植實時應用環境AEP
其中,POSIX 1003.1系列標準是POSIX最主體內容,也是我們最關心的部分。該系列內容由如下主體定義以及一些擴展和增補組成:
1003.1 1988年通過,基本OS接口
1003.1b 1993年通過,實時擴展
1003.1c 1995年通過,線程擴展
1003.1d 1999年通過,實時擴展
1003.1j 2000年通過,高級實時擴展
1003.1q 2000年通過,事件數據流跟蹤
三、測試POISX線程相關的函數
圖3.1 創建RTP工程
圖3.2 選擇編譯器
1、編寫一個測試代碼testpoisx.cpp
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <semLib.h>
typedef void* (*LPFUNCPTR)(void*);
struct counter
{
int i;
int j;
};
static bool __running =true;
static counter __counter = { 0, 0 };
pthread_mutex_t __mutex;
static void*
task1(void* arg)
{
struct timespec ts;
while(__running)
{
int rc = pthread_mutex_lock(&__mutex);
printf("Currentcounter i:%d\n", __counter.i++);
rc =pthread_mutex_unlock(&__mutex);
ts.tv_sec = 1;
ts.tv_nsec = 0;
nanosleep(&ts, 0);
}
return 0;
}
static void*
task2(void* arg)
{
struct timespec ts;
while(__running)
{
int rc = pthread_mutex_lock(&__mutex);
printf("Currentcounter j:%d\n", __counter.j++);
rc =pthread_mutex_unlock(&__mutex);
ts.tv_sec = 2;
ts.tv_nsec = 0;
nanosleep(&ts, 0);
}
return 0;
}
int
startTask(LPFUNCPTR func,
size_t stackSize,
const bool& realtimeScheduling,
const int& priority)
{
pthread_tid;
pthread_attr_t attr;
int rc = pthread_attr_init(&attr);
if (rc != 0)
{
return -1;
}
if (stackSize > 0)
{
if(stackSize < PTHREAD_STACK_MIN)
{
stackSize = PTHREAD_STACK_MIN;
}
rc= pthread_attr_setstacksize(&attr, stackSize);
if (rc != 0)
{
return -1;
}
}
if (realtimeScheduling)
{
rc= pthread_attr_setschedpolicy(&attr, SCHED_RR);
if (rc != 0)
{
return -1;
}
sched_param param;
param.sched_priority = priority;
rc= pthread_attr_setschedparam(&attr, ¶m);
if (rc != 0)
{
return -1;
}
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
}
rc =pthread_create(&id, &attr, func, NULL);
if (rc != 0)
{
return -1;
}
return id;
}
int
stopTask(constpthread_t& id)
{
int rc = pthread_detach(id);
return rc;
}
int
main(int argc, char* argv[])
{
int rc = pthread_mutex_init(&__mutex, 0);
if (rc != 0)
{
return -1;
}
int nId1 = startTask((LPFUNCPTR)task1, 4096,true, 100);
int nId2 = startTask((LPFUNCPTR)task2, 4096,true, 100);
while (__running)
{
if (getchar() == '\n')
{
__running= false;
break;
}
}
struct timespec ts;
ts.tv_sec= 2;
ts.tv_nsec= 0;
nanosleep(&ts,0);
if (nId1 > 0)
{
stopTask(nId1);
}
if (nId2 > 0)
{
stopTask(nId2);
}
rc =pthread_mutex_destroy(&__mutex);
if (rc != 0)
{
return -1;
}
return 0;
}
2、運行測試代碼
圖3.3 選擇vxsim0仿真
圖3.4 運行之後的結果
當程序代碼執行pthread_create時,返回結果始終爲“-1”,這裏你必須對VxWorks的POSIX過程要熟悉了。經過查詢有關錯誤代碼資料(#define ENOSYS 71 /*Function not implemented */),我們發現VxWorks提供的仿真器功能很基礎,但有很多組件未包含進來,於是我們需要定製自己的鏡像文件,詳細過程參考圖5.8-5.9。
圖3.5 兩條任務線程啓動
Current counter i:0
Current counter j:0
Current counter i:1
Current counter j:1
Current counter i:2
Current counter i:3
Current counter j:2
Current counter i:4
Current counter i:5
Current counter j:3
Current counter i:6
Current counter i:7
Current counter j:4
Current counter i:8
Current counter i:9
Current counter j:5
Current counter i:10
。。。
四、創建X86鏡像文件
圖4.1 創建VxWorks鏡像文件
圖4.2 工程名稱爲vxworks_i386
圖4.3 選擇BSP和編譯器
圖4.4 該選擇默認
圖4.5 選擇PROFILE_DEVELOPMENT
如圖4.5所示,我們選擇“PROFILE_DEVELOPMENT”主要後面用到剪裁包含的組件,具體怎麼操作,後面將會講到。
圖4.6 完成嚮導
圖4.7 鏡像文件樹型結構
我們打開usrAppInit.c文件,添加一句簡單的啓動信息,最後編譯鏡像工程。
/******************************************************************************
*
* usrAppInit - initialize theusers application
*/
voidusrAppInit (void)
{
#ifdefUSER_APPL_INIT
USER_APPL_INIT; /* for backwards compatibility */
#endif
/* addapplication specific code here */
printf("Hello World!\n");
}
五、創建新連接
圖5.1 新建一個仿真連接
圖5.2 選擇上述生成的鏡像文件
圖5.3 定製內存大小,此項默認即可
圖5.4 優先級爲belowNormal
圖5.5 設置目標服務選擇
圖5.6 設置路徑對象映射關係
圖5.7 完成嚮導
圖5.8 剪裁包含POSIX線程調度組件
圖5.9有效的POSIX線程組件
圖5.10 啓動連接vxsim2
六、創建libice130動態連接庫
如何創建動態連接庫及使用調用,參考之前《VxWorks6.6開發共享庫指南要點》文檔,然後工程注意事項如下:
編譯開關需要增加“-DVXWORKS -D__i386 -DICE_API_EXPORTS-Xrtti”
圖6.1 編譯選項
七、修改libice130源碼
這部分工作相對來說比較大,也是決定移植成功的關鍵階段。總共按照8個方面修改,主要是適應VxWorks開發環境而配置的內容。
1、修改include/IceE/Cond.h文件
#ifdef VXWORKS
Time tm = Time::now() + timeout;
timeval tv = tm.gettimeval();
#else
timeval tv = Time::now() + timeout;
#endif
2、修改include/IceE/Mutex.h文件
1)增加包含文件
#ifdef VXWORKS
#include<semLib.h>
#endif
2)修改Mutex::init(…)函數
inline void
Mutex::init(MutexProtocol protocol)
{
#if defined(__linux) &&!defined(__USE_UNIX98)
# ifdef NDEBUG
int rc = pthread_mutex_init(&_mutex, 0);
# else
pthread_mutexattr_t attr = { PTHREAD_MUTEX_ERRORCHECK_NP };
int rc = pthread_mutex_init(&_mutex, &attr);
# endif
if(rc != 0)
{
throw ThreadSyscallException(__FILE__,__LINE__, rc);
}
#elifdefined(VXWORKS)
//memset(&_mutex, '\0',sizeof(_mutex));
int rc = pthread_mutex_init(&_mutex,0);
assert(rc == 0);
if (rc != 0)
{
throw ThreadSyscallException(__FILE__,__LINE__, rc);
}
#else // !defined(__linux) || defined(__USE_UNIX98)
pthread_mutexattr_t attr;
int rc = pthread_mutexattr_init(&attr);
assert(rc == 0);
if(rc != 0)
{
throw ThreadSyscallException(__FILE__,__LINE__, rc);
}
//
// Enable mutex error checking in debug builds
//
#ifndef NDEBUG
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
assert(rc == 0);
if(rc != 0)
{
throw ThreadSyscallException(__FILE__,__LINE__, rc);
}
#endif
//
// If system has support for priority inheritance we set the protocol
// attribute of the mutex
//
#ifdefined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT >0
if(PrioInherit == protocol)
{
rc =pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
if(rc != 0)
{
throwThreadSyscallException(__FILE__, __LINE__, rc);
}
}
#endif
rc = pthread_mutex_init(&_mutex, &attr);
assert(rc == 0);
if(rc != 0)
{
throw ThreadSyscallException(__FILE__,__LINE__, rc);
}
rc = pthread_mutexattr_destroy(&attr);
assert(rc == 0);
if(rc != 0)
{
throw ThreadSyscallException(__FILE__,__LINE__, rc);
}
#endif
}
3)、修改include/IceE/NetWork.h文件
#ifdef _WIN32
# include <winsock2.h>
typedef int ssize_t;
#else
# include <unistd.h>
# include <fcntl.h>
# include <sys/socket.h>
# if defined(__hpux)
# include <sys/time.h>
# else
#ifndef VXWORKS
# include <sys/poll.h>
#endif
# include <sys/time.h>
# endif
3、修改include/IceE/Time.h文件
1)增加下面包含文件
#ifndef_WIN32_WCE
# if defined(_WIN32)
# include <sys/timeb.h>
# include <time.h>
# else
# include <sys/time.h>
# endif
#endif
2)修改Time類
#ifndef _WIN32
#ifdef VXWORKS
timeval gettimeval() const;
#else
operator timeval()const;
#endif
#endif
4、修改src/IceE/Instance.cpp文件
#ifdef _WIN32
# include <winsock2.h>
# ifndef _WIN32_WCE
# include <process.h>
# endif
#else
# include <signal.h>
#ifndef VXWORKS
# include <pwd.h>
#endif
# include <sys/types.h>
#endif
using namespace std;
using namespace Ice;
using namespace IceInternal;
…
if(!newUser.empty())
{
#ifndef VXWORKS
struct passwd* pw =getpwnam(newUser.c_str());
if(!pw)
{
SyscallExceptionex(__FILE__, __LINE__);
ex.error =getSystemErrno();
throw ex;
}
if(setgid(pw->pw_gid) ==-1)
{
SyscallExceptionex(__FILE__, __LINE__);
ex.error =getSystemErrno();
throw ex;
}
if(setuid(pw->pw_uid) ==-1)
{
SyscallExceptionex(__FILE__, __LINE__);
ex.error =getSystemErrno();
throw ex;
}
#endif
5、修改src/IceE/NetWork.cpp文件
1)修改包含文件
# include <sys/ioctl.h>
# include <net/if.h>
#ifdef VXWORKS
# include <ioLib.h>
# include <sockLib.h>
#define socklen_tint
#endif
# ifdef __sun
# include <sys/sockio.h>
# endif
#endif
2)修改setBlock(…)函數
void
IceInternal::setBlock(SOCKET fd,bool block)
{
if(block)
{
#ifdef _WIN32
unsigned long arg = 0;
if(ioctlsocket(fd, FIONBIO, &arg)== SOCKET_ERROR)
{
closeSocketNoThrow(fd);
SocketException ex(__FILE__, __LINE__);
ex.error = WSAGetLastError();
throw ex;
}
#elif VXWORKS
unsigned long lNonBlock = 0;
if (ioctl(fd, FIONBIO, &lNonBlock)== SOCKET_ERROR)
{
closeSocketNoThrow(fd);
SocketExceptionex(__FILE__, __LINE__);
ex.error= errno;
throwex;
}
#else
int flags = fcntl(fd, F_GETFL);
flags &= ~O_NONBLOCK;
if(fcntl(fd, F_SETFL, flags) ==SOCKET_ERROR)
{
closeSocketNoThrow(fd);
SocketException ex(__FILE__,__LINE__);
ex.error = errno;
throw ex;
}
#endif
}
else
{
#ifdef _WIN32
unsigned long arg = 1;
if(ioctlsocket(fd, FIONBIO, &arg)== SOCKET_ERROR)
{
closeSocketNoThrow(fd);
SocketException ex(__FILE__,__LINE__);
ex.error = WSAGetLastError();
throw ex;
}
#elif VXWORKS
unsigned long lNonBlock = 1;
if (ioctl(fd, FIONBIO, &lNonBlock)== SOCKET_ERROR)
{
closeSocketNoThrow(fd);
SocketExceptionex(__FILE__, __LINE__);
ex.error= errno;
throwex;
}
#else
int flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
if(fcntl(fd, F_SETFL, flags) ==SOCKET_ERROR)
{
closeSocketNoThrow(fd);
SocketException ex(__FILE__,__LINE__);
ex.error = errno;
throw ex;
}
#endif
}
}
3)修改createPipe (…)函數
void
IceInternal::createPipe(SOCKETfds[2])
{
#ifdef _WIN32
SOCKET fd = createSocket();
setBlock(fd, true);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
struct sockaddr_in* addrin = reinterpret_cast<structsockaddr_in*>(&addr);
addrin->sin_family = AF_INET;
addrin->sin_port = htons(0);
addrin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
doBind(fd, addr);
doListen(fd, 1);
try
{
fds[0] = createSocket();
}
catch(...)
{
::closesocket(fd);
throw;
}
try
{
setBlock(fds[0], true);
#ifndef NDEBUG
bool connected = doConnect(fds[0],addr);
assert(connected);
#else
doConnect(fds[0], addr);
#endif
}
catch(...)
{
// fds[0] is closed by doConnect
::closesocket(fd);
throw;
}
try
{
fds[1] = doAccept(fd);
}
catch(...)
{
::closesocket(fds[0]);
::closesocket(fd);
throw;
}
::closesocket(fd);
try
{
setBlock(fds[1], true);
}
catch(...)
{
::closesocket(fds[0]);
// fds[1] is closed by setBlock
throw;
}
#elifdefined(VXWORKS)
SOCKET fd = createSocket();
setBlock(fd, true);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
struct sockaddr_in* addrin =reinterpret_cast<struct sockaddr_in*>(&addr);
addrin->sin_family = AF_INET;
addrin->sin_port = htons(0);
addrin->sin_addr.s_addr =htonl(INADDR_LOOPBACK);
doBind(fd, addr);
doListen(fd, 1);
try
{
fds[0] = createSocket();
}
catch(...)
{
::close(fd);
throw;
}
try
{
setBlock(fds[0], true);
#ifndef NDEBUG
bool connected = doConnect(fds[0],addr);
assert(connected);
#else
doConnect(fds[0], addr);
#endif
}
catch(...)
{
// fds[0] is closed by doConnect
::close(fd);
throw;
}
try
{
fds[1] = doAccept(fd);
}
catch(...)
{
::close(fds[0]);
::close(fd);
throw;
}
::close(fd);
try
{
setBlock(fds[1], true);
}
catch(...)
{
::close(fds[0]);
// fds[1] is closed by setBlock
throw;
}
#else
if(::pipe(fds) != 0)
{
SyscallException ex(__FILE__,__LINE__);
ex.error = getSystemErrno();
throw ex;
}
try
{
setBlock(fds[0], true);
}
catch(...)
{
// fds[0] is closed by setBlock
closeSocketNoThrow(fds[1]);
throw;
}
try
{
setBlock(fds[1], true);
}
catch(...)
{
closeSocketNoThrow(fds[0]);
// fds[1] is closed by setBlock
throw;
}
#endif
}
6、修改src/IceE/Selector.h文件
1)修改包含文件與宏定義
#if defined(__linux) &&!defined(ICE_NO_EPOLL)
# define ICE_USE_EPOLL 1
#elif defined(__APPLE__) &&!defined(ICE_NO_KQUEUE)
# define ICE_USE_KQUEUE 1
#elif defined(_WIN32)
# define ICE_USE_SELECT 1
#elif defined(VXWORKS)
# define ICE_USE_SELECT 1
#endif
#if defined(ICE_USE_EPOLL)
#ifndef VXWORKS
# include <sys/epoll.h>
#endif
#elif defined(ICE_USE_KQUEUE)
# include <sys/event.h>
#else
# if !defined(ICE_USE_SELECT)
#ifndef VXWORKS
# include <sys/poll.h>
#endif
# endif
# include <list>
#endif
#ifdef VXWORKS
# include <strings.h>
#endif
#include<IceE/NormalUtil.h>
2)修改函數select(…)
int select()
{
while(true)
{
int ret;
_nSelectedReturned = 0;
_nSelected = 0;
#if defined(ICE_USE_EPOLL)
ret = epoll_wait(_queueFd,&_events[0], _events.size(), _timeout > 0 ? _timeout * 1000 : -1);
#elif defined(ICE_USE_KQUEUE)
assert(!_events.empty());
if(_timeout > 0)
{
struct timespec ts;
ts.tv_sec = _timeout;
ts.tv_nsec = 0;
ret = kevent(_queueFd, 0, 0,&_events[0], _events.size(), &ts);
}
else
{
ret = kevent(_queueFd, 0, 0,&_events[0], _events.size(), 0);
}
#elif defined(ICE_USE_SELECT)
fd_set* rFdSet =fdSetCopy(_selectedReadFdSet, _readFdSet);
fd_set* wFdSet =fdSetCopy(_selectedWriteFdSet, _writeFdSet);
fd_set* eFdSet =fdSetCopy(_selectedErrorFdSet, _errorFdSet);
int nFd = 0;
#ifndef WIN32
nFd = FD_SETSIZE;
#endif
if(_timeout > 0)
{
struct timeval tv;
tv.tv_sec = _timeout;
tv.tv_usec = 0;
ret = ::select(nFd, rFdSet, wFdSet, eFdSet, &tv); // The firstparameter is ignored on Windows
}
else
{
ret = ::select(nFd, rFdSet, wFdSet, eFdSet, 0); // The firstparameter is ignored on Windows
}
#else
ret = poll(&_pollFdSet[0], _pollFdSet.size(),_timeout > 0 ? _timeout * 1000 : -1);
#endif
if(ret == SOCKET_ERROR)
{
if(interrupted())
{
continue;
}
assert(false);
Ice::SocketException ex(__FILE__, __LINE__);
ex.error = getSocketErrno();
throw ex;
}
assert(ret >= 0);
_nSelected =static_cast<unsigned int>(ret);
if(_nSelected == 0)
{
assert(_timeout > 0);
_timeout = 0;
}
return _nSelected;
}
}
3)修改函數getNextSelected (…)函數
T* getNextSelected()
{
assert(_nSelected > 0);
#if defined(ICE_USE_EPOLL) ||defined(ICE_USE_KQUEUE)
if(_nSelectedReturned == _nSelected)
{
if(_count != _events.size())
{
_events.resize(_count);
}
return 0;
}
//
// Round robin for the filedescriptors.
//
T* larger = 0;
T* smallest = 0;
for(unsigned int i = 0; i < _nSelected; ++i)
{
#if defined(ICE_USE_EPOLL)
T* handler =reinterpret_cast<T*>(_events[i].data.ptr);
#else
T* handler =reinterpret_cast<T*>(_events[i].udata);
#endif
if(!handler) // _fdIntrRead
{
assert(_nSelectedReturned >0 && _interruptCount == 0);
continue;
}
if(handler->_fd > _lastFd&& (larger == 0 || handler->_fd < larger->_fd))
{
larger = handler;
}
if(smallest == 0 || handler->_fd< smallest->_fd)
{
smallest = handler;
}
}
++_nSelectedReturned;
if(larger)
{
_lastFd = larger->_fd;
return larger;
}
else
{
assert(smallest);
_lastFd = smallest->_fd;
return smallest;
}
#else
if(_nSelectedReturned == _nSelected)
{
return 0;
}
//
// Round robin for the filedescriptors.
//
SOCKET largerFd = INVALID_SOCKET;
SOCKET smallestFd = INVALID_SOCKET;
#if defined(ICE_USE_SELECT)
#ifdef VXWORKS
int nFdsetMaxCount =howmany(FD_SETSIZE, NFDBITS);
int nReadFdSetCount = 0;
int nWriteFdSetCount = 0;
int nErrorFdSetCount = 0;
for (int j = 0; j < nFdsetMaxCount;++j)
{
intnCount1 = IceUtil::getFdsetBits(_selectedReadFdSet.fds_bits[j]);
nReadFdSetCount+= nCount1;
int nCount2 =IceUtil::getFdsetBits(_selectedWriteFdSet.fds_bits[j]);
nWriteFdSetCount+= nCount2;
intnCount3 = IceUtil::getFdsetBits(_selectedErrorFdSet.fds_bits[j]);
nErrorFdSetCount+= nCount3;
}
if (nReadFdSetCount == 0 &&nWriteFdSetCount == 0 && nErrorFdSetCount == 0)
{
Ice::Errorout(_instance->initializationData().logger);
out << "select() inselector returned " << _nSelected << " but no filedescriptoris ready";
return 0;
}
int nfdSetCount = 0;
const fd_set* fdSet;
if (_nSelectedReturned <nReadFdSetCount)
{
fdSet = &_selectedReadFdSet;
nfdSetCount = nReadFdSetCount;
}
else if (_nSelectedReturned <nWriteFdSetCount + nReadFdSetCount)
{
fdSet = &_selectedWriteFdSet;
nfdSetCount = nWriteFdSetCount;
}
else
{
fdSet = &_selectedErrorFdSet;
nfdSetCount = nErrorFdSetCount;
}
for (u_short i = 0; i < FD_SETSIZE;++i)
{
if (!FD_ISSET(i, fdSet))
{
continue;
}
SOCKET fd = i;
#else
if(_selectedReadFdSet.fd_count == 0 &&_selectedWriteFdSet.fd_count == 0 && _selectedErrorFdSet.fd_count == 0)
{
Ice::Errorout(_instance->initializationData().logger);
out << "select() inselector returned " << _nSelected << " but nofiledescriptor is ready";
return 0;
}
const fd_set* fdSet;
if(_nSelectedReturned < _selectedReadFdSet.fd_count)
{
fdSet = &_selectedReadFdSet;
}
else if(_nSelectedReturned < _selectedWriteFdSet.fd_count +_selectedReadFdSet.fd_count)
{
fdSet = &_selectedWriteFdSet;
}
else
{
fdSet = &_selectedErrorFdSet;
}
for(u_short i = 0; i < fdSet->fd_count; ++i)
{
SOCKET fd = fdSet->fd_array[i];
#endif
#else
for(std::vector<struct pollfd>::const_iterator p =_pollFdSet.begin(); p != _pollFdSet.end(); ++p)
{
if(p->revents == 0)
{
continue;
}
SOCKET fd = p->fd;
#endif
if(fd == _fdIntrRead)
{
assert(_nSelectedReturned >0 && _interruptCount == 0);
continue;
}
assert(fd != INVALID_SOCKET);
if(fd > _lastFd &&(largerFd == INVALID_SOCKET || largerFd > fd))
{
largerFd = fd;
}
if(smallestFd == INVALID_SOCKET ||fd < smallestFd)
{
smallestFd = fd;
}
}
if(largerFd != INVALID_SOCKET)
{
_lastFd = largerFd;
}
else
{
assert(smallestFd !=INVALID_SOCKET);
_lastFd = smallestFd;
}
typename std::map<SOCKET, T*>::const_iterator q =_handlerMap.find(_lastFd);
if(q == _handlerMap.end())
{
Ice::Errorout(_instance->initializationData().logger);
out << "filedescriptor" << static_cast<int>(_lastFd) << " not registeredwith selector";
return 0;
}
++_nSelectedReturned;
return q->second;
#endif
}
7、修改src/IceE/Thread.cpp文件
1)修改包含文件與宏定義
#ifdef VXWORKS
#include<taskLib.h>
extern "C"int sysClkRateGet(void);
#endif
2)修改sleep(…)函數
void
IceUtil::ThreadControl::sleep(constTime& timeout)
{
#ifdef VXWORKS
struct timeval tv =timeout.gettimeval();
#else
struct timeval tv = timeout;
#endif
struct timespec ts;
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000L;
nanosleep(&ts, 0);
}
3)修改Time::now (…)函數
Time
IceUtil::Time::now(Clock clock)
{
#if defined(_WIN32_WCE)
//
// Note that GetTickCount returns the number of ms since the
// device was started. Time cannot be used to represent an
// absolute time on CE since GetLocalTime doesn't have millisecond
// resolution.
//
return Time(static_cast<Int64>(GetTickCount()) * 1000);
#else
if(clock == Realtime)
{
# if defined(_WIN32)
struct _timeb tb;
_ftime(&tb);
return Time(static_cast<Int64>(tb.time) * ICE_INT64(1000000) +tb.millitm * 1000);
# else
#ifdef VXWORKS
struct timespec ts;
if(clock_gettime(CLOCK_MONOTONIC,&ts) < 0)
{
assert(0);
throwIce::SyscallException(__FILE__, __LINE__, errno);
}
return Time(ts.tv_sec *ICE_INT64(1000000) + ts.tv_nsec / ICE_INT64(1000));
#else
struct timeval tv;
if(gettimeofday(&tv, 0) < 0)
{
assert(0);
throwIce::SyscallException(__FILE__, __LINE__, errno);
}
return Time(tv.tv_sec * ICE_INT64(1000000) + tv.tv_usec);
#endif
# endif
}
else
{
#if defined(_WIN32)
if(frequency > 0.0)
{
Int64 count;
if(!QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&count)))
{
assert(0);
throwIce::SyscallException(__FILE__, __LINE__, GetLastError());
}
returnTime(static_cast<Int64>(count / frequency * 1000000.0));
}
else
{
struct _timeb tb;
_ftime(&tb);
returnTime(static_cast<Int64>(tb.time) * ICE_INT64(1000000) + tb.millitm *1000);
}
#elif defined(__hpux) ||defined(__APPLE__)
//
// Platforms do not support CLOCK_MONOTONIC
//
struct timeval tv;
if(gettimeofday(&tv, 0) < 0)
{
assert(0);
throwIce::SyscallException(__FILE__, __LINE__, errno);
}
return Time(tv.tv_sec * ICE_INT64(1000000) + tv.tv_usec);
#else
struct timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
{
assert(0);
throwIce::SyscallException(__FILE__, __LINE__, errno);
}
return Time(ts.tv_sec * ICE_INT64(1000000) + ts.tv_nsec /ICE_INT64(1000));
#endif
}
#endif
}
4)修改Time:: timeval (…)函數
#ifndef _WIN32
#ifdef VXWORKS
timevalIceUtil::Time::gettimeval() const
{
timeval tv;
tv.tv_sec =static_cast<long>(_usec / 1000000);
tv.tv_usec =static_cast<long>(_usec % 1000000);
return tv;
}
#else
IceUtil::Time::operator timeval()const
{
timeval tv;
tv.tv_sec = static_cast<long>(_usec / 1000000);
tv.tv_usec = static_cast<long>(_usec % 1000000);
return tv;
}
#endif
#endif
8、創建兩個文件
1)src/IceE/ NormalUtil.h文件
//**********************************************************************
//
//Copyright (c) 2015
//
//2015.02.11
// 平臺軟件部, 上海
// AllRights Reserved
//
//**********************************************************************
#ifndef_NORMALUTIL_H_
#define_NORMALUTIL_H_
namespaceIceUtil
{
ICE_APIint getFdsetBits(unsigned int nFd);
};
#endif// _NORMALUTIL_H_
2)src/IceE/ NormalUtil.cpp文件
//**********************************************************************
//
//Copyright (c) 2015
//
//2015.02.11
// 平臺軟件部, 上海
// AllRights Reserved
//
//**********************************************************************
#include<IceE/Config.h>
#include<IceE/NormalUtil.h>
staticconst unsigned char bits_table[256] =
{
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3,4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3,4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4,5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4,4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4,5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4,5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3,4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4,5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4,5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4,5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6,7, 7, 8
};
int
IceUtil::getFdsetBits(unsignedint nFd)
{
int nCount = 0;
nCount = bits_table[nFd & 0xff] + \
bits_table[(nFd >> 8) & 0xff];
return nCount;
}
八、測試範例
這是Ice1.3.0本身自帶的例子,稍微加以修改。兩個工程都是RTP爲基礎,具體如何引用so,以及相關的工程配置,請參考《VxWorks6.6開發共享庫指南要點》文檔。
編譯開關需要增加“-DVXWORKS -D__i386-DICE_API_EXPORTS”
1、創建服務端程序
//**********************************************************************
//
//Copyright (c) 2003-2008 ZeroC, Inc. All rights reserved.
//
// Thiscopy of Ice-E is licensed to you under the terms described in the
//ICEE_LICENSE file included in this distribution.
//
//**********************************************************************
#include <IceE/IceE.h>
#include "HelloI.h"
using namespace std;
int
run(int argc, char*argv[], const Ice::CommunicatorPtr&communicator)
{
if(argc> 1)
{
fprintf(stderr, "%s: too many arguments\n", argv[0]);
returnEXIT_FAILURE;
}
Ice::ObjectAdapterPtr adapter =communicator->createObjectAdapter("Hello");
Demo::HelloPtr object = new HelloI;
adapter->add(object,communicator->stringToIdentity("hello"));
adapter->activate();
communicator->waitForShutdown();
returnEXIT_SUCCESS;
}
int
main(int argc, char*argv[])
{
int status;
Ice::CommunicatorPtr communicator;
try
{
Ice::InitializationData initData;
initData.properties =Ice::createProperties();
initData.properties->load("/d/casco800A/fep/config/config.server");
communicator = Ice::initialize(argc,argv, initData);
status = run(argc, argv, communicator);
}
catch(const Ice::Exception& ex)
{
fprintf(stderr, "%s\n", ex.toString().c_str());
status = EXIT_FAILURE;
}
if(communicator)
{
try
{
communicator->destroy();
}
catch(const Ice::Exception& ex)
{
fprintf(stderr, "%s\n", ex.toString().c_str());
status = EXIT_FAILURE;
}
}
returnstatus;
}
2、創建客戶端程序
//**********************************************************************
//
//Copyright (c) 2003-2008 ZeroC, Inc. All rights reserved.
//
// Thiscopy of Ice-E is licensed to you under the terms described in the
//ICEE_LICENSE file included in this distribution.
//
//**********************************************************************
#include <IceE/IceE.h>
#include "Hello.h"
using namespace std;
using namespace Demo;
void
menu()
{
printf("usage:\n");
printf("t:send greeting as twoway\n");
printf("o:send greeting as oneway\n");
#ifdef ICEE_HAS_BATCH
printf("O:send greeting as batch oneway\n");
printf("f:flush all batch requests\n");
#endif
printf("T:set a timeout\n");
printf("P:set a server delay\n");
printf("s:shutdown server\n");
printf("x:exit\n");
printf("?:help\n");
}
int
run(int argc, char*argv[], const Ice::CommunicatorPtr&communicator)
{
if(argc> 1)
{
fprintf(stderr, "%s: too many arguments\n", argv[0]);
returnEXIT_FAILURE;
}
Ice::PropertiesPtr properties =communicator->getProperties();
const char* proxyProperty = "Hello.Proxy";
string proxy = properties->getProperty(proxyProperty);
if(proxy.empty())
{
fprintf(stderr, "%s: property `%s' not set\n", argv[0],proxyProperty);
returnEXIT_FAILURE;
}
Ice::ObjectPrx base =communicator->stringToProxy(proxy);
HelloPrx twoway = HelloPrx::checkedCast(base->ice_twoway()->ice_timeout(-1));
if(!twoway)
{
fprintf(stderr, "%s: invalid proxy\n", argv[0]);
returnEXIT_FAILURE;
}
HelloPrx oneway = twoway->ice_oneway();
#ifdef ICEE_HAS_BATCH
HelloPrx batchOneway = twoway->ice_batchOneway();
#endif
int timeout= -1;
int delay =0;
menu();
char c =EOF;
do
{
try
{
printf("==>"); fflush(stdout);
do
{
c = getchar();
}
while(c!= EOF && c =='\n');
if(c== 't')
{
twoway->sayHello(delay);
}
elseif(c =='o')
{
oneway->sayHello(delay);
}
#ifdef ICEE_HAS_BATCH
elseif(c =='O')
{
batchOneway->sayHello(delay);
}
elseif(c =='f')
{
batchOneway->ice_flushBatchRequests();
}
#endif
elseif(c =='T')
{
if(timeout== -1)
{
timeout = 2000;
}
else
{
timeout = -1;
}
twoway = twoway->ice_timeout(timeout);
oneway =oneway->ice_timeout(timeout);
#ifdef ICEE_HAS_BATCH
batchOneway =batchOneway->ice_timeout(timeout);
#endif
if(timeout== -1)
{
printf("timeoutis now switched off\n");
}
else
{
printf("timeout is now set to 2000ms\n");
}
}
elseif(c =='P')
{
if(delay== 0)
{
delay = 2500;
}
else
{
delay = 0;
}
if(delay== 0)
{
printf("serverdelay is now deactivated\n");
}
else
{
printf("server delay is now set to 2500ms\n");
}
}
elseif(c =='s')
{
twoway->shutdown();
}
elseif(c =='x')
{
//Nothing to do
}
elseif(c =='?')
{
menu();
}
else
{
printf("unknowncommand `%c'\n", c);
menu();
}
}
catch(const Ice::Exception& ex)
{
fprintf(stderr, "%s\n", ex.toString().c_str());fflush(stderr);
}
}
while(c !=EOF && c != 'x');
returnEXIT_SUCCESS;
}
int
main(int argc, char*argv[])
{
int status;
Ice::CommunicatorPtr communicator;
try
{
Ice::InitializationData initData;
initData.properties =Ice::createProperties();
initData.properties->load("/d/casco800A/fep/config/config.client");
communicator = Ice::initialize(argc,argv, initData);
status = run(argc, argv, communicator);
}
catch(const Ice::Exception& ex)
{
fprintf(stderr, "%s\n", ex.toString().c_str());
status = EXIT_FAILURE;
}
if(communicator)
{
try
{
communicator->destroy();
}
catch(const Ice::Exception& ex)
{
fprintf(stderr, "%s\n", ex.toString().c_str());
status = EXIT_FAILURE;
}
}
returnstatus;
}
3、運行測試
3.1、執行客戶與服務程序
圖8.1 並行運行的程序以不同顏色標識
3.2、查看端口使用狀況
1)啓動服務端時的狀態
-> netstat "-ap tcp"
INET sockets
Prot Fd Recv-Q Send-Q Local Address Foreign Address State
TCP 46 0 0 localhost.49176 localhost.49175 ESTABLISHED
TCP 47 0 0 localhost.49175 localhost.49176 ESTABLISHED
TCP 49 0 0 localhost.49178 localhost.49177 ESTABLISHED
TCP 50 0 0 localhost.49177 localhost.49178 ESTABLISHED
TCP 51 0 0 localhost.10000 0.0.0.0.* LISTEN
TCP 53 0 0 localhost.49180 localhost.49179 ESTABLISHED
TCP 54 0 0 localhost.49179 localhost.49180 ESTABLISHED
INET6 sockets
Prot Fd Recv-Q Send-Q Local Address Foreign Address State
value = 0 = 0x0
->
2)啓動客戶端時的狀態
-> netstat "-ap tcp"
INET sockets
Prot Fd Recv-Q Send-Q Local Address Foreign Address State
TCP 46 0 0 localhost.49176 localhost.49175 ESTABLISHED
TCP 47 0 0 localhost.49175 localhost.49176 ESTABLISHED
TCP 49 0 0 localhost.49178 localhost.49177 ESTABLISHED
TCP 50 0 0 localhost.49177 localhost.49178 ESTABLISHED
TCP 51 0 0 localhost.10000 0.0.0.0.* LISTEN
TCP 53 0 0 localhost.49180 localhost.49179 ESTABLISHED
TCP 54 0 0 localhost.49179 localhost.49180 ESTABLISHED
TCP 56 0 0 localhost.49182 localhost.49181 ESTABLISHED
TCP 57 0 0 localhost.49181 localhost.49182 ESTABLISHED
TCP 59 0 0 localhost.49184 localhost.49183 ESTABLISHED
TCP 60 0 0 localhost.49183 localhost.49184 ESTABLISHED
TCP 61 0 0 localhost.49185 localhost.10000 ESTABLISHED
TCP 62 0 0 localhost.10000 localhost.49185 ESTABLISHED
INET6 sockets
Prot Fd Recv-Q Send-Q Local Address Foreign Address State
value = 0 = 0x0
->
3.3、控制檯輸出結果
1)testclient.vxe控制檯
usage:
t: send greeting as twoway
o: send greeting as oneway
O: send greeting as batch oneway
f: flush all batch requests
T: set a timeout
P: set a server delay
s: shutdown server
x: exit
?: help
==> t
輸入命令t,回車
2)testIce130控制檯
Hello World!
現在你看到上面的輸出結果,這是否令人激動呢?!
九、總結
本文詳細描述了基於POSIX移植可能出現的問題,修改的代碼並不是很大,我相信按照上述的步驟,移植iceE1.3.0到VxWorks操作系統下並不是難事了,你可以省下zeroC公司商業方面的技術支持費用了。
你可能發現本文並沒使用ice的makefile自動編譯,爲了簡便移植工作,防止在vxWorks開發環境中安裝第三方的庫過於複雜。因此,我們只要從Windows下拷貝已經生成的源碼,在此基礎上再進行修改移植即可。
我們已經成功地移植了 IceE1.3.0,如何在VxWorks操作系統中正確部署,那就看你的架構了。