相关知识:
1。 内核对象主要有:
存取符号对象、事件对象、文件对象、文件映射对象、I/O完成端口对象、作业对象、信箱对象、互斥对象、管道对象、进程对象、信标对象、线程对象
2。内核对象的数据结构只能被内核访问,因此应用程序无法在内存中访问这些数据结构并直接改变它们的内容,必须通过调用Windows接口来对内核对象进行操作。
3。使用内核对象命名可以让进程给跨越进程边界访问, Microsoft没有提供为内核对象赋予命名的指导原则,所有这些对象都共享单个命名空间。
Real Instance:
using MFC6.0
在一次C++网络编程中,我自定义了一个ExSocket类继承了CAsyncSocket类,由于发送接收的数据量比较大,我采用了数据缓冲,一个线程负责接收数据并存储到缓冲中,另外一个线程负责从缓冲中取出数据并处理,在存取缓冲采用CMutex互斥锁互斥访问。典型的生产者和消费者的关系:
file ExSocket.h:
class ExSoket: public CAsyncSocket
{
private :
CMutex *pMutex;//mutex for critical section
MessageNode *pMessageHead;//Head of data list
MessageNode *pMessageTail;//Tail of data list
//other variables
public:
ExSocket();//the constructor
//other functions
}
file ExSocket.cpp:
ExSocket::ExSocket()
{
//initialization of the class
pMutex=new CMutex(FALSE,"ExSocketMutex");
}
//other definition of the functions
当我写完这个类后,在不同的机器建立连接进行功能测试,一切都正常,以为万无一失了,谁知道拿到实际中给其他人调用的时候却出现错误,当时百思不得其解,最后没有办法,只好进行调试跟踪,发现出错是执行到CSingleLock lock(pMutex);的时候发生的,原因是pMutex指针为空,明明在构造函数里分配了,为什么会变为空指针呢?检查程序也没有其他地方错误释放啊(这个指针是在析构函数里释放的)。
到最后还发现第一次使用这个类是不会出错的,只要第一次使用后的类对象没有释放,第二次使用就会出错,认真查看代码原来错误就出现在pMutex=new CMutex(FALSE,"ExSocketMutex");
这个语句粗略一看使用没有错误,MSDN也没有说明什么能导致它错误的,但是它第二次执行的时候new返回的值是NULL,哪怕第一次使用是在另外一个进程!也就是第一个进程的使用竟然影响到第二个进程!
出现这种情况只能是new CMutex(FALSE,"ExSocketMutex");这个语句涉及到了Windows内核,因为内核对象可以跨边界访问,而这个互斥对象恰好是通过命名"ExSocketMutex"实现跨边界访问的,也就是第一个进程创建了一个"ExSocketMutex"后,第二个进程可以通过"ExSocketMutex"名称来访问它但如果继续创建这个互斥对象就会失败!(请参考《Windows核心编程》第三章)
所以操作内核对象不能用常规的编程思路去进行。
这个互斥怎么办呢?不能去掉互斥啊,可以用以下的方法解决:
file ExSocket.cpp:
ExSocket::ExSocket()
{
//initialization of the class
CString s;
s.Format(_T("ExSocketMutex%d"),this);//also using sprintf(str,"ExSocketMutex%d",this); for char array str
pMutex=new CMutex(FALSE,(LPCSTR)s);
}
//other definition of the functions
由于每个类对象不可能使用相同的地址空间,所有对Mutex的命名就不会重复,这样就不会因为创建多个类对象而产生冲突了。但如果要在进程间互斥还是必须使用相同的命名的。