怎么利用设计模式来更有效的使用共享内存
2009-01-19 14:37
411 查看
Linux中处理来自共享对象的同步事件
怎么利用设计模式来更有效的使用共享内存 级别:中等在
I
O
bjectWithEvents
类接口中包含了定义了
OnEvent()
方法的
I
E
ventSink
这个嵌入类,这是一个接受发送者
pid
和字符串消息的事件处理器。
getInstance()
方法返回一个共享内存中的对象的引用,
AddEventHandler()
是注册一个事件处理器,
SendMessage()
发送一个消息到对象上,没有任何共享内存的引用,下面的
Listing2
中列出了
IobjectWithEvents
的程序代码:1 Listing 2. shm-client1.cpp 2 3 #include <iostream> 4 #include <sys/types.h> 5 #include <unistd.h> 6 #include "common.h" 7 8 #define HERE __FILE__ << ":" << __LINE__ << " " 9 10 using namespace std; 11 12 class EventSink : public IObjectWithEvents::IEventSink13 {14 public:15 void OnEvent(pid_t pid, const char * msg)16 {17 cout << HERE << "Message from pid(" << pid << ")/t : " << msg << endl;18 }19 };20 21 22 int main()23 {24 IObjectWithEvents * powe = IObjectWithEvents::getInstance(); 25 26 EventSink sink;27 powe->AddEventHandler(&sink); 28 29 powe->SendMessage();30 return 0;31 }
EventSink
类提供了事件处理器的实现,在主函数中显示了发送消息和处理事件的标准顺序。
Listing3
中列出了
ObjectWithEvents
的典型实现代码:1 Listing 3. ObjectWithEvents.h 2 3 #include "common.h" 4 5 class ObjectWithEvents : public IObjectWithEvents 6 { 7 public: 8 // We assume singleton design pattern for illustration 9 static ObjectWithEvents * ms_pObjectWithEvents; 10 11 ObjectWithEvents(); 12 13 //the implementation for IObjectWithEvents 14 void FireEvent(); 15 virtual bool AddEventHandler(IEventSink * pEI); 16 virtual void SendMessage(); 17 18 //Collection for maintaining events 19 enum { MAX_EVENT_HANDLERS = 16, }; 20 long m_npEI; 21 IEventSink * m_apEI[MAX_EVENT_HANDLERS]; 22 pid_t m_alPID[MAX_EVENT_HANDLERS]; 23 }; 24 25 26 Listing 4. ObjectWithEvents.cpp 27 28 #include <iostream> 29 #include <sys/types.h> 30 #include <sys/shm.h> 31 #include <unistd.h> 32 #include <pthread.h> 33 #include "ObjectWithEvents.h" 34 35 using namespace std; 36 37 ObjectWithEvents * ObjectWithEvents::ms_pObjectWithEvents = NULL; 38 39 IObjectWithEvents * IObjectWithEvents::getInstance() 40 { 41 // the following commented code is for illustration only. 42 43 /* 44 if (NULL == ObjectWithEvents::ms_pObjectWithEvents) 45 { 46 ObjectWithEvents::ms_pObjectWithEvents = new ObjectWithEvents(); 47 } 48 */ 49 50 return ObjectWithEvents::ms_pObjectWithEvents; 51 } 52 53 ObjectWithEvents::ObjectWithEvents() : m_npEI(0) 54 { 55 56 } 57 58 59 void ObjectWithEvents::FireEvent() 60 { 61 // iterate through the collection 62 for (long i = 0; i < m_npEI; i++) 63 { 64 //Recheck for NULL 65 if (0 != m_apEI[i]) 66 { 67 // Fire the event 68 m_apEI[i]->OnEvent(m_alPID[i], ""); 69 } 70 } 71 72 return; 73 } 74 75 bool ObjectWithEvents::AddEventHandler(IEventSink * pEI) 76 { 77 // NULL check 78 if (NULL == pEI) 79 { 80 return false; 81 } 82 83 // check if there is space for this event handler 84 if (MAX_EVENT_HANDLERS == m_npEI) 85 { 86 return false; 87 } 88 89 // Add this event handler to the collection 90 m_alPID[m_npEI] = getpid(); 91 m_apEI[m_npEI++] = pEI; 92 93 return true; 94 } 95 96 void ObjectWithEvents::SendMessage() 97 { 98 //Some processing 99 //And then fire the event 100 101 FireEvent(); 102 103 return;104 } 你能使用下面的命令行来编译这些例子程序:
g++-g-oshm_clientshm_client1.cppObjectWithEvents.cpp当你运行shm_client时,将得到下面的输出:
$./shm_clientshm_client1.cpp:16Messagefrompid(3920)
:使用共享内存:没有事件缓存 现在,对于在共享内存中实例ObjectWithEvents的实现作了以下的修改。 1 Listing 5. Changes to ObjectWithEvents.cpp 2 3 4 // To add a declaration for the "new" operator: 5 class ObjectWithEvents : public IObjectWithEvents{ 6 public: void * operator new(unsigned int); 7 }; 8 9 // To include an additional header for the Initializer class:10 11 #include "Initializer.h"12 13 14 // To overload the operator "new":15 16 void * ObjectWithEvents::operator new(unsigned int)17 {18 return ms_pObjectWithEvents;19 }20 21 22 // Then, FireEvent is completely changed:23 24 25 void ObjectWithEvents::FireEvent()26 {27 // We need to serialize all access to the collection by more than one process28 int iRetVal = Initializer::LockMutex(); 29 30 if (0 != iRetVal)31 {32 return;33 } 34 35 pid_t pid = getpid(); 36 37 // iterate through the collection and fire only events belonging to the current process38 for (long i = 0; i < m_npEI; i++)39 {40 // Check whether the handler belongs to the current process.41 if (pid != m_alPID[i])42 {43 continue;44 } 45 46 //Recheck for NULL47 if (0 != m_apEI[i])48 {49 m_apEI[i]->OnEvent(pid, "");50 }51 } 52 53 // release the mutex54 if ((0 == iRetVal) && (0 != Initializer::UnlockMutex()))55 {56 // Deal with error.57 } 58 59 return;60 }61 62 63 // The following are changes to ObjectWithEvents::AddEventHandler(): 64 65 // 1. Before accessing the collection, we lock the mutex: 66 67 int bRetVal = Initializer::LockMutex();68 69 if (0 != bRetVal)70 {71 return false;72 }73 74 75 // 2. After accessing the collection, we release the mutex: 76 77 if ((0 == bRetVal) && (0 != Initializer::UnlockMutex()))78 {79 // Deal with error.80 } 在共享内存中的示例化对象,定义了一个叫做
Initializer
的额外的类1 Listing 6. Initializer.h 2 3 #ifndef __Initializer_H__ 4 #define __Initializer_H__ 5 6 class Initializer 7 { 8 public : 9 int m_shmid;10 static Initializer ms_Initializer;11 Initializer(); 12 13 static pthread_mutex_t ms_mutex;14 static int LockMutex();15 static int UnlockMutex();16 }; 17 18 #endif // __Initializer_H__
Initializer
定义了共享内存
id
的变量
m_shmid
和对于同步事件处理器的一个信号量变量
ms_mutex.
LockMutex()
锁定互斥体,
UnlockMutex()
则解锁互斥体。
Listing7
列出了
Initializer
的实现代码:1 Listing 7. Initializer.cpp 2 3 #include <iostream> 4 #include <sys/types.h> 5 #include <sys/shm.h> 6 #include <unistd.h> 7 #include <pthread.h> 8 #include "Initializer.h" 9 #include "ObjectWithEvents.h" 10 11 using namespace std;12 13 Initializer Initializer::ms_Initializer; 14 15 pthread_mutex_t Initializer::ms_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; 16 17 Initializer::Initializer() : m_shmid(-1)18 {19 bool bCreated = false;20 key_t key = 0x1234; 21 22 m_shmid = shmget(key,sizeof(ObjectWithEvents), 0666); 23 24 if (-1 == m_shmid)25 {26 if(ENOENT != errno)27 {28 cerr<<"Critical Error"<<endl;29 return;30 } 31 32 m_shmid = shmget(key, sizeof(ObjectWithEvents), IPC_CREAT|0666); 33 34 if (-1 == m_shmid )35 {36 cout << " Critical Error " << errno<< endl;37 return;38 }39 40 bCreated = true;41 } 42 43 ObjectWithEvents::ms_pObjectWithEvents = (ObjectWithEvents*)shmat(m_shmid,NULL,0); 44 45 if (NULL == ObjectWithEvents::ms_pObjectWithEvents)46 {47 cout << " Critical Error " << errno << endl;48 return;49 }50 51 if (true == bCreated)52 {53 ObjectWithEvents * p = new ObjectWithEvents();54 }55 56 // Create a mutex with no initial owner. 57 58 pthread_mutex_init(&ms_mutex, NULL);59 }60 61 62 63 int Initializer::LockMutex()64 {65 // Request ownership of mutex.66 67 pthread_mutex_lock(&ms_mutex); 68 69 if(EDEADLK == errno)70 {71 cout << "DeadLock" << endl;72 return -1;73 }74 75 return 0;76 } 77 78 int Initializer::UnlockMutex()79 {80 return pthread_mutex_unlock(&ms_mutex);81 } 如果共享内存不存在的话则创建它,并在共享内存里做成共享对象。如果共享内存已经存在的话,则略过构造共享对象。Initializer::m_shmid纪录标识符
ObjectWithEvents::ms_pObjectWithEvents并记录共享对象的引用。 即使在所有的进程从这个共享内存分离(detach)也不释放共享内存。让你用ipcrm命令显示的销毁它并能用ipcs命令快速察看共享内存的信息。用下面的命令编译成可执行程序:
g++-g-oshm_clientshm_client1.cppObjectWithEvents.cppInitializer.cpp
控制台上会有下面那样的输出信息:Listing8.Theconsoledump
$./shm_client
shm_client1.cpp:16Messagefrompid(4332) :
$ipcs
------SharedMemorySegments--------
key shmid owner perms bytes nattch status
0x00001234327686 sachin 666 136 0
$./shm_client
shm_client1.cpp:16Messagefrompid(4333) :
$ipcrm-m327686
ObjectWithEvents
实例中有一个能从很多进程中收集事件的收集器。它仅仅能发送当前进程注册的事件。设计模式中表明了以下的两点:任何访问事件收集器都要被互斥体对象保护 在发送之前事件都要通过进程ID进行过滤。 IPC的共享内存和事件缓存 现在,让我们看看进程间通信的共享内存和事件缓存。如果事件被缓存到共享对象里,则他们在稍后会被过滤。接收的进程将根据事件查询共享对象。然后,通过一个同步模型,进程间的通信能被接受到,这是开发下面的设计模式的主要动机。
在
IobjectWithEvents
中,像下面那样增加一对儿方法: 1 Listing 9. Adding methods to IobjectWithEvents 2 3 class IObjectWithEvents 4 { 5 public: 6 7 virtual bool EnqueueEvent(const char * msg) = 0; 8 virtual bool PollForEvents() = 0; 9 }; 10
EnqueueEvent()简单的增加到共享对象中的事件缓存中,并且
PollForEvents()将接收这个缓存。shm_client1将像下面那样被使用
EnqueueEvent
():
powe->EnqueueEvent("Messagefromshm_client1");shm_client2(实质上是shm_client1的拷贝)将像下面那样使用
PollForEvents()
:
powe->EnqueueEvent("Messagefromshm_client2");powe->PollForEvents();
ObjectWithEvents
的实现代码是下面那样:Listing10.AdditionstoObjectWithEvents 1 class ObjectWithEvents : public IObjectWithEvents 2 { 3 public: 4 virtual bool EnqueueEvent(const char * msg); 5 virtual bool PollForEvents(); 6 7 //The event cache 8 enum { MAX_EVENTS = 16, MAX_EVENT_MSG = 256, }; 9 long m_nEvents;10 pid_t m_alPIDEvents[MAX_EVENTS];11 char m_aaMsgs[MAX_EVENTS][MAX_EVENT_MSG];12 }; 13 14 15 新的构造函数变成:
ObjectWithEvents::ObjectWithEvents():m_npEI(0),m_nEvents(0){}
EnqueueEvent()存储事件(例如,每一个被发送的事件的消息和进程号)到一个队列中,
PollForEvents()则迭代整个队列并为队列中的事件一个一个的调用OnEvent().Listing11.EnqueueEvent 1 bool ObjectWithEvents::EnqueueEvent(const char * msg) 2 { 3 if (NULL == msg) 4 { 5 return false; 6 } 7 8 if (MAX_EVENTS == m_nEvents) 9 {10 //IEventSink collection full11 return false;12 }13 14 int bRetVal = Initializer::LockMutex();15 16 if (0 != bRetVal)17 {18 return false;19 }20 21 m_alPIDEvents[m_nEvents] = getpid();22 strncpy(m_aaMsgs[m_nEvents++], msg, MAX_EVENT_MSG - 1);23 24 if ((0 == bRetVal) && (0 != Initializer::UnlockMutex()))25 {26 // Deal with error.27 }28 29 return true;30 }31 32 33 bool ObjectWithEvents::PollForEvents()34 {35 if (0 == m_nEvents)36 {37 return true;38 }39 40 int bRetVal = Initializer::LockMutex();41 42 if (0 != bRetVal)43 {44 return false;45 }46 47 pid_t pid = getpid();48 49 for (long i = 0; i < m_npEI; i++)50 {51 // Does the handler belongs to current process ? 52 53 if (pid != m_alPID[i])54 {55 continue;56 }57 58 //Recheck for NULL59 60 if (0 == m_apEI[i])61 {62 continue;63 }64 65 for (long j = 0; j < m_nEvents; j++)66 {67 m_apEI[i]->OnEvent(m_alPIDEvents[j], m_aaMsgs[j]);68 }69 }70 71 if ((0 == bRetVal) && (0 != Initializer::UnlockMutex()))72 {73 // Deal with error.74 } 75 76 return true;77 } 现在使用下面的命令变成新命令:
g++-g-oshm_client1shm_client1.cppObjectWithEvents.cppInitializer.cpp
g++-g-oshm_client2shm_client2.cppObjectWithEvents.cppInitializer.cpp
在你的控制台上将像下面那样输出消息:Listing12.Outputfromshm_client1andshm_client2
$./shm_client1
$./ipcs
------SharedMemorySegments--------
key shmid owner perms bytes nattch status
0x00001234360454 sachin 666 4300 0
$./shm_client2
shm_client2.cpp:16Messagefrompid(4454) :Messagefromshm_client1
shm_client2.cpp:16Messagefrompid(4456) :Messagefromshm_client2[/code]下载
Description | Name | Size | Downloadmethod |
Sharedmemorysamplecode | sync_code.zip | 4KB |
相关文章推荐
- 使用 IBM 中间件实现 SaaS 解决方案,第 4 部分: 单一实例多租户应用程序中资源共享的设计模式
- /LGC设计模式/进程间通信之共享内存
- 使用 IBM 中间件实现 SaaS 解决方案,第 4 部分: 单一实例多租户应用程序中资源共享的设计模式
- 设计模式之享元模式,内存共享模式,Flyweight
- 使用简单工厂模式来进行Python的设计模式编程
- 吐血原创:mini2440和win7笔记本利用无路由功能的交换机共享上网(使用x-router软路由)
- 使用策略设计模式,反射,解决商城系统中的商品折扣问题
- 实战设计模式——使用IoC模式(控制反转或依赖注入)实现松散耦合设计(1)
- 使用mmap函数进行内核空间和用户空间的共享内存通信
- 使用mmap函数进行内核空间和用户空间的共享内存通信
- 章节 2.1 可靠的软件 – 灵活,可靠的软件 使用设计模式和敏捷开发
- 深入解析Python设计模式编程中建造者模式的使用
- 实例讲解Python设计模式编程之工厂方法模式的使用
- VS2005中使用水晶报表PUSH模式完成主从表设计
- 如何使用设计模式来构造系统--(3)
- 设计模式之适配器模式(换个包装再度利用)
- 使用Micrisoft.net设计方案 第三章Web表示模式 Web模式集群详细介绍 Observer(观察器)
- Java常用设计模式与使用场景
- Unity设计模式之装饰模式的使用
- 设计模式中的组合模式在JavaScript程序构建中的使用