QNX下三类定时器的C++封装

QNX下存在三类定时器,使用的难易程度不一样,精度也不一样。分别对其进行了一下封装,使用起来相对简单一点。
一、PtTimer

PtTimer是三者之中使用起来最简单的,但性能也最差,不过它与Rttimer一样,可以直接操作GUI里面的函数,相比HardTimer就不行了。

  1. // QnxPtTimer.h: interface for the CQnxPtTimer class. 
  2. // 
  3. // 
  4. #if !defined(AFX_QNXPTTIMER_H__01609CA6_BF75_4F50_93B6_BF323F927AB6__INCLUDED_) 
  5. #define AFX_QNXPTTIMER_H__01609CA6_BF75_4F50_93B6_BF323F927AB6__INCLUDED_ 
  6. /*这个类基于PtTimer控间,存在一些实时性的不足。如英文描述。
  7. PtTimer is easy to use, but doesn't give accurate timer events. In particular, 
  8. it doesn't guarantee a constant repeat rate; since the repetition is handled by rearming 
  9. the timer for each event, any delays in handling the events accumulate. Kernel timers 
  10. guarantee an accurate repeat rate even if your application can't keep up with them. 
  11. */
  12. #if _MSC_VER > 1000 
  13. #pragma once 
  14. #endif // _MSC_VER > 1000 
  15. #include  
  16. #include  
  17. #include  
  18. //毫秒为单位 
  19. class CQnxPtTimer  
  20. {
  21. public:
  22.     CQnxPtTimer(unsigned long init=1,//开始时间 
  23.         unsigned long repeat=500,//重复周期 
  24.         bool iAutoStart=0/*是否自动启动,1自动启动,0手动启动*/);
  25.     virtual ~CQnxPtTimer();
  26. private:
  27.     void CreatePareantWin();
  28. public:
  29.     void SetInitTime(unsigned long init);
  30.     void SetRepeatTime(unsigned long repeat);
  31.     
  32.     void StartTimer();
  33.     void StopTimer();
  34.     int  SetPtTimerParent(PtWidget_t* pParent); 
  35. public:
  36.     PtWidget_t *GetTimer();
  37.     unsigned long GetInitTime();
  38.     unsigned long GetRepeatTime();
  39. public:
  40.     static int Cb_Timer( PtWidget_t *widget, void *data,  PtCallbackInfo_t *cbinfo );
  41.     
  42. public:
  43.     virtual void RepeatTimerThread();
  44.     void SetTimerPareant(PtWidget_t *pParent);
  45. protected:
  46.     PtWidget_t  *m_pPareantWin;
  47.     PtWidget_t  *m_pPtTimer;
  48.     //The time, in milliseconds: 
  49.     unsigned long m_InitTime;   //The time,before the first timer callback is activated 
  50.     unsigned long m_RepeatTime; //The time, for the repeat rate of the timer once the initial time period has expired.  
  51. };
  52. #endif // !defined(AFX_QNXPTTIMER_H__01609CA6_BF75_4F50_93B6_BF323F927AB6__INCLUDED_) 
  53. // QnxPtTimer.cpp: implementation of the CQnxPtTimer class. 
  54. // 
  55. // 
  56. #include "QnxPtTimer.h" 
  57. // 
  58. // Construction/Destruction 
  59. // 
  60. CQnxPtTimer::CQnxPtTimer(unsigned long init,unsigned long repeat,bool iAutoStart)
  61. {
  62.     m_pPtTimer=NULL;
  63.     CreatePareantWin();
  64.     m_pPtTimer=PtCreateWidget(PtTimer,m_pPareantWin,0,NULL);
  65.     SetInitTime(init);
  66.     SetRepeatTime(repeat);
  67.     PtAddCallback(m_pPtTimer, Pt_CB_TIMER_ACTIVATE,Cb_Timer, this );    
  68.     if(iAutoStart)
  69.     {
  70.         StartTimer();
  71.     }
  72. }
  73. CQnxPtTimer::~CQnxPtTimer()
  74. {
  75.     PtUnrealizeWidget(m_pPareantWin);
  76. }
  77. void CQnxPtTimer::SetTimerPareant(PtWidget_t *pParent)
  78. {
  79.     if (m_pPtTimer)
  80.     {
  81.         m_pPareantWin=pParent;
  82.         PtReparentWidget(m_pPtTimer,m_pPareantWin);
  83.     }
  84. }
  85. void CQnxPtTimer::CreatePareantWin()
  86. {
  87.     m_pPareantWin=PtCreateWidget(PtWindow,Pt_NO_PARENT,0,NULL);
  88.     PhPoint_t pos={-200,-200};
  89.     PtSetResource(m_pPareantWin,Pt_ARG_POS,&pos,0);
  90. }
  91. void CQnxPtTimer::SetInitTime(unsigned long init)
  92. {
  93.     m_InitTime=init;
  94.     if (m_pPtTimer)
  95.     {
  96.         PtSetResource(m_pPtTimer,Pt_ARG_TIMER_INITIAL,m_InitTime,0);
  97.     }
  98. }
  99. void CQnxPtTimer::SetRepeatTime(unsigned long repeat)
  100. {
  101.     m_RepeatTime=repeat;
  102.     if (m_pPtTimer)
  103.     {
  104.         PtSetResource(m_pPtTimer,Pt_ARG_TIMER_REPEAT,m_RepeatTime,0);
  105.     }
  106. }
  107.     
  108. void CQnxPtTimer::StartTimer()
  109. {
  110.     if (!m_pPtTimer)
  111.     {
  112.         return;
  113.     }
  114.     if (m_InitTime==0)
  115.     {
  116.         m_InitTime=500;
  117.     }
  118.     PtSetResource(m_pPtTimer,Pt_ARG_TIMER_INITIAL,m_InitTime,0);
  119.     PtRealizeWidget(m_pPareantWin); 
  120.     PtWindowToBack(m_pPareantWin);
  121. }
  122. void CQnxPtTimer::StopTimer()
  123. {
  124.     if (!m_pPtTimer)
  125.     {
  126.         return;
  127.     }
  128.     PtSetResource(m_pPtTimer,Pt_ARG_TIMER_INITIAL,0,0);
  129. }
  130. PtWidget_t *CQnxPtTimer::GetTimer()
  131. {
  132.     return m_pPtTimer;
  133. }
  134. unsigned long CQnxPtTimer::GetInitTime()
  135. {
  136.     return m_InitTime;
  137. }
  138. unsigned long CQnxPtTimer::GetRepeatTime()
  139. {
  140.     return m_RepeatTime;
  141. }
  142. int CQnxPtTimer::Cb_Timer( PtWidget_t *widget, void *data,  PtCallbackInfo_t *cbinfo )
  143. {
  144.     CQnxPtTimer *pPtTim=(CQnxPtTimer *)data;
  145.     if (pPtTim!=NULL)
  146.     {
  147.         pPtTim->RepeatTimerThread();
  148.     }
  149.     return Pt_CONTINUE;
  150. }
  151. int  CQnxPtTimer::SetPtTimerParent(PtWidget_t* pParent)
  152. {
  153.     return PtReparentWidget( m_pPtTimer,pParent);
  154. }   
  155. void CQnxPtTimer::RepeatTimerThread()
  156. {
  157. //  printf("CQnxPtTimer::RepeatTimerThread/n"); 
  158. }

二、Rttimer

  1. // QnxRtTimer.h: interface for the CQnxRtTimer class.
  2. //
  3. //
  4. #if !defined(AFX_QNXRTTIMER_H__C504E91B_54FC_46D2_AE62_74CBE0AE5636__INCLUDED_)
  5. #define AFX_QNXRTTIMER_H__C504E91B_54FC_46D2_AE62_74CBE0AE5636__INCLUDED_
  6. #if _MSC_VER > 1000
  7. #pragma once
  8. #endif // _MSC_VER > 1000
  9. #include 
  10. #include 
  11. #include 
  12. class CQnxRtTimer  
  13. {
  14. public:
  15.     CQnxRtTimer(unsigned long init=1,//开始时间
  16.         unsigned long repeat=500,//重复周期
  17.         bool iAutoStart=0/*是否自动启动,1自动启动,0手动启动*/);
  18.     virtual ~CQnxRtTimer();
  19. private:
  20.     void    MyTimerCreate();
  21.     unsigned long   GetTv_Sec(unsigned long imSec);//由毫秒转化为秒(不到一秒的舍去)
  22.     unsigned long   GetTv_nSec(unsigned long imSec);//由毫秒转化为纳秒(只取不到一秒的毫秒部分)
  23. public:
  24.     void SetInitTime(unsigned long init);
  25.     void SetRepeatTime(unsigned long repeat);
  26.     
  27.     void StartTimer();
  28.     void StopTimer();
  29. public:
  30.     unsigned long GetInitTime();
  31.     unsigned long GetRepeatTime();
  32. public:
  33.     static int Cb_RtTimer( RtTimer_t *timer, void *data);
  34. public:
  35.     virtual void RepeatTimerThread( );
  36. protected:
  37.     //The time, in milliseconds:
  38.     unsigned long m_InitTime;   //The time,before the first timer callback is activated
  39.     unsigned long m_RepeatTime; //The time, for the repeat rate of the timer once the initial time period has expired. 
  40.     struct itimerspec m_itTimerSpec;
  41.     RtTimer_t *   m_pRtTimer;
  42. };
  43. #endif // !defined(AFX_QNXRTTIMER_H__C504E91B_54FC_46D2_AE62_74CBE0AE5636__INCLUDED_)
  44. // QnxRtTimer.cpp: implementation of the CQnxRtTimer class.
  45. //
  46. //
  47. #include "QnxRtTimer.h"
  48. //
  49. // Construction/Destruction
  50. //
  51. CQnxRtTimer::CQnxRtTimer(unsigned long init,unsigned long repeat,bool iAutoStart)
  52. {
  53.     m_InitTime=init;
  54.     m_RepeatTime=repeat;
  55.     m_pRtTimer=NULL;
  56.     MyTimerCreate();
  57.     if (iAutoStart)
  58.     {
  59.         StartTimer();
  60.     }
  61. }
  62. CQnxRtTimer::~CQnxRtTimer()
  63. {
  64. }
  65. void    CQnxRtTimer::MyTimerCreate()
  66. {
  67. //  printf("CQnxRtTimer::MyTimerCreate/n");
  68.     m_pRtTimer=RtTimerCreate(CLOCK_REALTIME,-1,Cb_RtTimer,this);
  69. }
  70. //由毫秒转化为秒(不到一秒的舍去)
  71. unsigned long   CQnxRtTimer::GetTv_Sec(unsigned long imSec)
  72. {
  73.     return (imSec/1000) ;
  74. }
  75. //由毫秒转化为纳秒(只取不到一秒的毫秒部分)
  76. unsigned long   CQnxRtTimer::GetTv_nSec(unsigned long imSec)
  77. {
  78.     return (imSec%1000)*1000000;
  79. }
  80. void CQnxRtTimer::SetInitTime(unsigned long init)
  81. {
  82.     m_InitTime=init;
  83. }
  84. void CQnxRtTimer::SetRepeatTime(unsigned long repeat)
  85. {
  86.     m_RepeatTime=repeat;
  87. }
  88. void CQnxRtTimer::StartTimer()
  89. {
  90. //  printf("CQnxRtTimer::StartTimer/n");
  91.     if (m_InitTime==0)//--------消除m_InitTime=0对定时器启动的影响
  92.     {
  93.         m_InitTime=1;
  94.     }
  95.     /*
  96.     struct timespec it_value 
  97.     A timespec structure that contains the amount of time left before the timer expires, or zero if the timer is disarmed. This value is expressed as the relative interval until expiration, even if the timer was armed with an absolute time. 
  98.     struct timespec it_interval 
  99.     A timespec structure that contains the timer's reload value. If nonzero, it indicates a repetitive timer period. 
  100.     */
  101.     // Set the timer to trigger every repeat msec
  102.     m_itTimerSpec.it_value.tv_sec = GetTv_Sec(m_InitTime); 
  103.     m_itTimerSpec.it_value.tv_nsec = GetTv_nSec(m_InitTime); 
  104.     m_itTimerSpec.it_interval.tv_sec = GetTv_Sec(m_RepeatTime);
  105.     m_itTimerSpec.it_interval.tv_nsec =GetTv_nSec(m_RepeatTime); 
  106.     // Activate the timer
  107.     RtTimerSetTime(m_pRtTimer, 0,  &m_itTimerSpec, NULL);
  108. }
  109. void CQnxRtTimer::StopTimer()
  110. {
  111.     // 
  112.     m_itTimerSpec.it_value.tv_sec = 0; 
  113.     m_itTimerSpec.it_value.tv_nsec =0; 
  114.     RtTimerSetTime(m_pRtTimer, 0,  &m_itTimerSpec, NULL);
  115. }
  116. unsigned long CQnxRtTimer::GetInitTime()
  117. {
  118.     return m_InitTime;
  119. }
  120. unsigned long CQnxRtTimer::GetRepeatTime()
  121. {
  122.     return  m_RepeatTime;
  123. }
  124. int CQnxRtTimer::Cb_RtTimer( RtTimer_t *timer, void *data)
  125. {
  126. //  printf("CQnxRtTimer::Cb_RtTimer/n");
  127.     CQnxRtTimer *pRttimer=(CQnxRtTimer *)data;
  128.     if (!pRttimer)
  129.     {
  130.         return Pt_CONTINUE;
  131.     }
  132.     pRttimer->RepeatTimerThread();
  133.     return Pt_CONTINUE;
  134. }
  135. void CQnxRtTimer::RepeatTimerThread( )
  136. {
  137. //  printf("CQnxRtTimer::RepeatTimerThread/n");
  138. }
3、HardTimer

 

 

  1. // QnxHardTimer.h: interface for the CQnxHardTimer class.
  2. //
  3. //
  4. /******************************************************************************
  5.     Timers in a separate process from the GUI -- necessary for hard realtime
  6.     毫秒为单位
  7.     注意,这个线程与Photon GUI是独立的,在这个定时器线程里不能够直接操作GUI
  8. 界面程序里的资源,必须使用PtEnter等API,可参考“Parallel Operations”一章
  9. 里的描述。如下是使用中的一个示例:
  10.     int eval;
  11.     if ((eval = PtEnter(0)) < 0 && eval != -EDEADLK){
  12.       fprintf( stderr, "Couldn't enter: %s/n", strerror( -eval ) );
  13.     }else{
  14.         //操作Photon GUI资源,如:
  15.         PtSetResource(g_pProgress,Pt_ARG_GAUGE_VALUE ,iCount,0);
  16.         //操作完毕离开
  17.         PtLeave(eval);  // does nothing if eval == -EDEADLK
  18.     }   
  19.     该定时器封装为以毫秒为单位,可以满足一般的程序设计需要。如果需要更高
  20. 精度的定时器,只需简单更改构造函数部分。                                
  21. ******************************************************************************/
  22. #if !defined(AFX_QNXHARDTIMER_H__B6E1FA73_C325_4A30_8F2E_67891C02BC1B__INCLUDED_)
  23. #define AFX_QNXHARDTIMER_H__B6E1FA73_C325_4A30_8F2E_67891C02BC1B__INCLUDED_
  24. #if _MSC_VER > 1000
  25. #pragma once
  26. #endif // _MSC_VER > 1000
  27. #include 
  28. #include 
  29. #include 
  30. #include 
  31. // Define the codes for the pulses
  32. #define PULSE_HARDTIMER         _PULSE_CODE_MINAVAIL
  33. #define PULSE_EXIT              _PULSE_CODE_MINAVAIL + 1
  34. typedef union {
  35.         struct _pulse   pulse;
  36.         /* your other message structures would go here too,此处不需要 */
  37. } _THREAD_MESSAGE;
  38. class CQnxHardTimer  
  39. {
  40. public:
  41.     CQnxHardTimer(unsigned long init=1,//开始时间
  42.         unsigned long repeat=500,//重复周期
  43.         bool iAutoStart=0/*是否自动启动,1自动启动,0手动启动*/);
  44.     virtual ~CQnxHardTimer();
  45. private:
  46.     void    TimerThreadCreate();
  47.     unsigned long   GetTv_Sec(unsigned long imSec);//由毫秒转化为秒(不到一秒的舍去)
  48.     unsigned long   GetTv_nSec(unsigned long imSec);//由毫秒转化为纳秒(只取不到一秒的毫秒部分)
  49. public:
  50.     void SetInitTime(unsigned long init);
  51.     void SetRepeatTime(unsigned long repeat);
  52.     
  53.     void StartTimer();
  54.     void StopTimer();
  55.     void ExitTimerThread();
  56. public:
  57.     unsigned long GetInitTime();
  58.     unsigned long GetRepeatTime();
  59. public:
  60.     static void*  _PeriodicThread(void *vArguments);
  61.     
  62. public:
  63.     virtual void  RepeatTimerThread();
  64. protected:
  65.     //The time, in milliseconds:
  66.     unsigned long m_InitTime;   //The time,before the first timer callback is activated
  67.     unsigned long m_RepeatTime; //The time, for the repeat rate of the timer once the initial time period has expired. 
  68.     int           m_iTimerChannel;
  69.     struct itimerspec m_itTimerSpec;
  70.     timer_t           m_TimerId;
  71.     //PC定时器用到的变量
  72.     struct sigevent  m_seEvent;
  73. };
  74. #endif // !defined(AFX_QNXHARDTIMER_H__B6E1FA73_C325_4A30_8F2E_67891C02BC1B__INCLUDED_)
  75. // QnxHardTimer.cpp: implementation of the CQnxHardTimer class.
  76. //
  77. //
  78. #include "QnxHardTimer.h"
  79. //#include 
  80. //
  81. // Construction/Destruction
  82. //
  83. CQnxHardTimer::CQnxHardTimer(unsigned long init,unsigned long repeat,bool iAutoStart)
  84. {
  85.     m_InitTime=init;
  86.     m_RepeatTime=repeat;
  87.     TimerThreadCreate();
  88.     if (iAutoStart)
  89.     {
  90.         StartTimer();
  91.     }
  92. }
  93. CQnxHardTimer::~CQnxHardTimer()
  94. {
  95. }
  96. void    CQnxHardTimer::TimerThreadCreate()
  97. {
  98.     
  99.     pthread_t ptThread;         //定时器线程
  100.     pthread_attr_t ptaThreadAttributes;
  101.     m_iTimerChannel = ChannelCreate(0);
  102.     // Set the timer to send a pulse with the value PULSE_HARDTIMER when triggered
  103.     m_seEvent.sigev_notify = SIGEV_PULSE;
  104.     m_seEvent.sigev_coid = ConnectAttach(ND_LOCAL_NODE, 0, m_iTimerChannel, _NTO_SIDE_CHANNEL, 0);
  105.     m_seEvent.sigev_priority = getprio(0);
  106.     m_seEvent.sigev_code = PULSE_HARDTIMER;
  107.     timer_create(CLOCK_REALTIME, &m_seEvent, &m_TimerId);
  108.     // Create the thread and make it detached - no need to call join or cancel
  109.     pthread_attr_init( &ptaThreadAttributes );
  110.     pthread_attr_setdetachstate(&ptaThreadAttributes, PTHREAD_CREATE_DETACHED );
  111.     pthread_create(&ptThread, &ptaThreadAttributes, &_PeriodicThread, this );
  112. }
  113. //由毫秒转化为秒(不到一秒的舍去)
  114. unsigned long   CQnxHardTimer::GetTv_Sec(unsigned long imSec)
  115. {
  116.     return (imSec/1000) ;
  117. }
  118. //由毫秒转化为纳秒(只取不到一秒的毫秒部分)
  119. unsigned long   CQnxHardTimer::GetTv_nSec(unsigned long imSec)
  120. {
  121.     return (imSec%1000)*1000000;
  122. }
  123. void CQnxHardTimer::SetInitTime(unsigned long init)
  124. {
  125.     m_InitTime=init;
  126. }
  127. void CQnxHardTimer::SetRepeatTime(unsigned long repeat)
  128. {
  129.     m_RepeatTime=repeat;
  130. }
  131. void CQnxHardTimer::StartTimer()
  132. {
  133.     if (m_InitTime==0)//--------消除m_InitTime=0对定时器启动的影响
  134.     {
  135.         m_InitTime=1;
  136.     }
  137.     /*
  138.     struct timespec it_value 
  139.     A timespec structure that contains the amount of time left before the timer expires, or zero if the timer is disarmed. This value is expressed as the relative interval until expiration, even if the timer was armed with an absolute time. 
  140.     struct timespec it_interval 
  141.     A timespec structure that contains the timer's reload value. If nonzero, it indicates a repetitive timer period. 
  142.     */
  143.     // Set the timer to trigger every repeat msec
  144.     m_itTimerSpec.it_value.tv_sec = GetTv_Sec(m_InitTime); 
  145.     m_itTimerSpec.it_value.tv_nsec = GetTv_nSec(m_InitTime); 
  146.     m_itTimerSpec.it_interval.tv_sec = GetTv_Sec(m_RepeatTime);
  147.     m_itTimerSpec.it_interval.tv_nsec =GetTv_nSec(m_RepeatTime); 
  148.     // Activate the timer
  149.     timer_settime(m_TimerId, 0,  &m_itTimerSpec, NULL);
  150. }
  151. void CQnxHardTimer::StopTimer()
  152. {
  153.     // 
  154.     m_itTimerSpec.it_value.tv_sec = 0; 
  155.     m_itTimerSpec.it_value.tv_nsec =0; 
  156. //  m_itTimerSpec.it_interval.tv_sec = GetTv_Sec(m_RepeatTime);
  157. //  m_itTimerSpec.it_interval.tv_nsec =GetTv_nSec(m_RepeatTime); 
  158.     // Activate the timer
  159.     timer_settime(m_TimerId, 0,  &m_itTimerSpec, NULL);
  160. }
  161. //
  162. void CQnxHardTimer::ExitTimerThread()
  163. {
  164.     // Send the destruct message
  165.     MsgSendPulse(m_seEvent.sigev_coid, 1, PULSE_EXIT, PULSE_EXIT);
  166. }
  167. unsigned long CQnxHardTimer::GetInitTime()
  168. {
  169.     return m_InitTime;
  170. }
  171. unsigned long CQnxHardTimer::GetRepeatTime()
  172. {
  173.     return  m_RepeatTime;
  174. }
  175. /*
  176.  * Function:     _PeriodicThread      
  177.  * Description:  定时器定时产生一个pulse,该线程就是捕捉该周期性的pulse
  178.  * Input:          
  179.  * Return:         
  180.  * DateTime:     Mar 4, 2005   8:49:46 AM
  181.  * Others:         
  182.  */
  183. void*  CQnxHardTimer::_PeriodicThread(void *vArguments)
  184. {
  185. //  printf("CQnxHardTimer::_PeriodicThread/n");
  186.     CQnxHardTimer *pHtimer=(CQnxHardTimer *)vArguments;
  187.     _THREAD_MESSAGE tmMessage;
  188.     int iReceiveIdentifier;
  189.     // Infinite loop   
  190.     while(1)
  191.     {
  192.         // Receive the message
  193.         iReceiveIdentifier = MsgReceive(pHtimer->m_iTimerChannel, &tmMessage,sizeof(_THREAD_MESSAGE), NULL);
  194.                                                 
  195.         // Check if this is a pulse message
  196.         if(iReceiveIdentifier == 0)
  197.         {
  198.             // Check if this is the timer pulse
  199.             if(tmMessage.pulse.code == PULSE_HARDTIMER)//时间基准
  200.             {
  201.                 // Perform periodic code here
  202.                 pHtimer->RepeatTimerThread();
  203. /*              //可以在这里新启动一个线程来处理,例如
  204.                 pthread_attr_t attr;                
  205.                 //线程初始化
  206.                 pthread_attr_init( &attr );
  207.                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED );
  208.                 
  209.                 //启动秒定时器线程
  210.                 pthread_create( NULL, &attr, &_SecondTimerThread, NULL );
  211.                 //启动整点定时器线程
  212.                 pthread_create( NULL, &attr, &_WholeHourTimerThread, NULL );
  213. */
  214.             }
  215.             if(tmMessage.pulse.code == PULSE_EXIT)
  216.             {
  217.                 // Break the loop
  218. //              printf("Destroying thread/n");
  219.                 break;
  220.             }
  221.         }
  222.     }
  223.     
  224.     // Display the destruction message
  225. //  printf("Destroying thread/n");
  226.     return 0;
  227. }
  228. void CQnxHardTimer::RepeatTimerThread()
  229. {
  230. //  printf("CQnxHardTimer::RepeatTimerThread/n");
  231. }

四、使用方法

使用时只需要从上述类里继承一个子类,并在子类里实现RepeatTimerThread就可以了。http://blog.csdn.net/Delores/archive/2008/10/23/3127978.aspx里的CCpuUsed 就是一个例子。

 

 

你可能感兴趣的:(QNX)