??xml version="1.0" encoding="utf-8" standalone="yes"?> 存在于内核别的一U资源,׃n内存指在多处理器?a target="_blank">计算机系l?/font>中,可以被不同中央处理器QCPUQ访问的大容量内?/span>。由于多个CPU需要快速访问存储器Q这样就要对存储器进行缓存(CacheQ。Q何一个缓存的数据被更新后Q由于其他处理器也可能要存取Q共享内存就需要立x斎ͼ否则不同的处理器可能用到不同的数据。共享内?(shared memory)?Unix下的多进E之间的通信Ҏ ,q种Ҏ通常用于一个程序的多进E间通信Q实际上多个E序间也可以通过׃n内存来传递信息?br /> lock的初始gؓ0Q当一个进E想q入临界区时Q?/span>先查?/span>lock的|若ؓ1Q说明已有进E在临界?/span>内,只好循环{待。等它变成了0Q才可进入?/span> 基本思想Q每个进E严格地按照轮流的顺序来q入临界区?/span> 优点Q保证在M时刻最多只有一个进E在临界?/span> 当一个进E想q入临界区时Q先调用enter_region函数Q判断是否能安全q入Q不能的话等待;当它从界区退出后Q需调用leave_region函数Q允许其它进E进入界区。两个函数的参数均ؓq程受?img style="width: 553px; height: 283px" border="0" alt="" src="http://www.shnenglu.com/images/cppblog_com/cass/q程同步5.jpg" width="553" height="283" /> 当一个进E想要进入它的界区Ӟ首先查一下是否允许它q入Q?/span>若允许,q接进入了Q若不允许,在那里循环地等待,一直等到允许它q入?br /> ~点Q?/span>
阅读全文
]]>
一个是要对U程的创建和撤消q行理Q另一个是要对U程对资源的讉K实施同步 。
阅读全文
]]>
例如Q:当进E正在运行的时候,q程内核对象处于未通知状态,当进E终止运行的时候,它就变ؓ已通知状态。进E内核对象中是个布尔|当对象创建时Q该D初始化ؓFALSEQ未通知状态)。当q程l止q行Ӟ操作pȝ自动对应的对象布尔值改为TRUEQ表C对象已经得到通知。当U程l止q行Ӟ操作pȝ会自动将U程对象的状态改为已通知状态。因此,可以相同的Ҏ用于应用E序Q以定U程是否不再q行?
阅读全文
]]>
?当一个线E需要将某个d已经完成的情况通知另外一个或多个U程时?
阅读全文
]]>
Q?Q、一个线E堆栈,用于l护U程执行时所需的所有函数参数和局部变量?
阅读全文
]]>
基本机制是:pȝ预留的一块全局׃n内存Q可用于被各q程暂时存储数据。写入进E首先创Z个全局内存块,q将数据写到该内存块Q接受数据的q程通过剪脓板机制获取此内存块的句柄Qƈ完成对该内存块数据的d?br />
道包括三种:
道(Pipe)实际是用于进E间通信的一D共享内存,创徏道的进E称为管道服务器Q连接到一个管道的q程为管道客h。一个进E在向管道写入数据后Q另一q程可以从道的另一端将其读取出来。匿名管?Anonymous Pipes)是在父进E和子进E间单向传输数据的一U未命名的管道,只能在本地计机中用,而不可用于网l间的通信?br /> 1)普通管?/span>PIPE, 通常有种限制,一是半双工,只能单向传输; 二是只能?a target="_blank">父子或者兄弟进E间使用.
2)管?/span>s_pipe: 去除了第一U限?可以双向传输.
3)?/span>?/span>道:name_pipe, 去除了第二种限制,可以在许多ƈ不相关的q程之间q行通讯.
邮g槽:
邮g?Mailslots)提供q程?span style="color: red">单向通信能力QQ何进E都能徏立邮件槽成ؓ邮g槽服务器。其它进E,UCؓ邮g槽客P可以通过邮g槽的名字l邮件槽服务器进E发送消息。进来的消息一直放在邮件槽中,直到服务器进E读取它为止。一个进E既可以是邮件槽服务器也可以是邮件槽客户Q因此可建立多个邮g槽实现进E间的双向通信?/span>
通过邮g槽可以给本地计算Z的邮件槽、其它计机上的邮g槽或指定|络区域中所有计机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超q?00字节Q非q播消息的长度则受邮件槽服务器指定的最大消息长度的限制?br /> 邮g槽与命名道怼Q不q它传输数据?span style="color: red">通过不可靠的数据?如TCP/IP协议中的UDP?完成?/span>Q一旦网l发生错误则无法保证消息正确地接Ӟ而命名管道传输数据则是徏立在可靠q接基础上的。不q邮件槽有简化的~程接口和给指定|络区域内的所有计机q播消息的能力,所以邮件槽不失为应用程序发送和接收消息的另一U选择?br />
优缺点:
邮槽最大的一个缺点便是只允许从客h到服务器Q徏立一U不可靠的单向数据通信?br />而另一斚wQ邮槽最大的一个优点在于,它们使客h应用能够非常Ҏ地将q播消息发送给一个或多个服务器应用?br />
׃n内存Q?br />
2、当两个或者多个进E访问共享资源时Q如何确保他们不会相互妨?----q程互斥问题?br />
原因Q?span style="font-family: 宋体; color: #000000; font-size: 12pt; language: zh-CN; text-combine: letters; mso-ascii-font-family: 'Times New Roman'; mso-fareast-font-family: 宋体; mso-bidi-font-family: +mn-cs; mso-font-kerning: 12.0pt">q程宏观上ƈ发执行,依靠旉中断来实现微观上轮流执行。当两个或者多个进E对同一个共享内存访问,l果不能预测?span style="font-family: 宋体; color: #000000; font-size: 12pt; language: zh-CN; text-combine: letters; mso-ascii-font-family: 'Times New Roman'; mso-fareast-font-family: 宋体; mso-bidi-font-family: +mn-cs; mso-font-kerning: 12.0pt">在同一时刻Q只允许一个进E访问该׃n数据Q?/span>卛_果当前已有一个进E正在用该数据Q那?/span>其他q程暂时不能讉K。这是互斥的概c?br />实现互斥讉K的四个条Ӟ
Q?Q、用标志位加锁?
~点是:lock也是一个共享资源,当进E竞争lockӞ可能会出现问题?/span>加锁标志位法的缺点在于可?/span>出现针对׃n变量 lock 的竞?/span>状态。例如,当进E?/span> 0 执行?/span>循环判断语句后,被时钟中?/span>打断Q从而可能多个q程?/span>时进入界区?/span>
是一U不安全的做法?/span>
Q?Q、强制轮法
~点Q违反了互斥讉K四条件中的第三个条gQ?span style="font-family: 宋体; color: #000000; font-size: 12pt; language: zh-CN; text-combine: letters">当一个进E运行在它的临界区外面时Q?/span>不能妨碍其他的进E进入界区
Q?Q?span style="font-family: 'Times New Roman'; color: #333333; font-size: 12pt; font-weight: normal; language: en-US; text-combine: letters; mso-ascii-font-family: 'Times New Roman'; mso-fareast-font-family: 黑体; mso-bidi-font-family: +mn-cs; mso-font-kerning: 12.0pt; mso-color-index: 1">PetersonҎ?/span>
结Q?br />
1Q浪?/span>CPU旉Q?br /> 2Q可能导致预料之?/span>的结果(如:一个低优先U进E位于界区中,q时有一个高优先U的q程也试图进入界区Q?br />
3、当q程间存在某U依存关pLQ如何来调整他们q行的先后次?----q程同步问题?br />用PQV原语操作实现同步Q略Q?/span>
另外Q上q的问题也适合U程吗?Q?nbsp;
]]>
①?span style="color: red">一个内核对?/span>Q操作系l用它来理q程。内核对象也是系l保存进E统计信息的地方?br />②?span style="color: red">一个地址I间Q其中包含所有执行体QexecutableQ或DLL模块的代码和数据。此外,它还包含动态内存分配,比如U程堆栈和堆的分配?br />q程与线E的关系Q?/span>
①、一个进E创建的时候,pȝ会自动创建它的第一个线E,q称ZU程Qprimary threadQ?br />②、进E要做Q何事情,都必让一个线E在它的上下文中q行。如果没有线E要执行q程地址I间包含的代码,q程失Ml箋存在的理由。所以,pȝ会自动销毁进E及其地址I间?br />③、一个进E可以有多个U程Q所有线E都在进E的地址I间?#8220;同时”执行代码。ؓ此,每个U程都有它自q一lCPU寄存器和它自q堆栈。对于所有要q行的线E,操作pȝ会轮ؓ每个U程调度一些CPU旉。它会采取round-robinQ轮询或轮流Q方式,为每个线E都分配旉片,从而营造出所有线E都?#8220;q发”q行的假象?br />
2、系l如何创Z个进E内核对象来理每个q程?br />当一个进E被初始化时Q系l要为它分配一个句柄表(I的Q也是用来理q程的内核对?。该句柄表只用于内核对象Q而不用于用户对象和gdi对象)。句柄表是一个数据结构的数组Q每个结构都包含一个指向内核对象的指针Q一?span class="Apple-converted-space"> 讉K屏蔽(DWORD)和一个标?DWORD)?br />Q:Q当q程中的U程调用创徏内核对象的函敎ͼ比如 CreatFileMapping)Ӟ内核׃ؓ该对象分配一个内存块q对它初始化?strong>同时对进E的句柄表进行扫描,扑և一个空,填充内核对象数据l构的内存地址到该的指针成员Q设|访问屏蔽和标志?br />Q:Q?/strong> 用于创徏内核对象的所有函数均q回与进E相关的句柄。该句柄实际上是攑օq程的句柄表中的索引 Q由此可知,句柄是与q程相关的,不能由其他进E直接成功地使用)。但q只适用部分pȝQ句柄的含义可能随时变更?span class="Apple-converted-space"> 应用E序在运行时有可能泄漏内核对象,但是当进E终止时pȝ能保所有内容均被正地清除。这个情况也适用于所有对象,资源和内存块Q也是说当q程l止q行Ӟpȝ保证进E不会留 下Q何对象?/span>
3、如何利用与一个进E关联的内核对象来操U该q程?br />
4、进E的各种不同的属性(或特性)Q以及用于查询和更改q些属性的几个函数?br />实例句柄、前一个实例句柄、进E的命o行、进E的环境变量、进E当前所在的驱动器和目录、还有版本问题等
5、如何利用一些函数在pȝ中创建或生成额外的进E?br />我们用CreateProcess函数来创Z个进E,参考MSDN。当一个线E调用CreateProcessӞpȝ会做如下工作Q?br />Q?Q、系l将创徏一个进E内核对?/span>Q其初始使用计数?span style="color: red">1。进E内核对象不是进E本w,而是操作pȝ用来理q个q程的一个小型数据结构(该内核对象是用来理新进E的Q?br />Q?Q、系l?span style="color: red">为新q程创徏一个虚拟地址I间Qƈ执行体文gQ和所有必要的DLLQ的代码及数据加载到q程的地址I间?br />Q?Q、系l?span style="color: red">为新q程的主U程创徏一个线E内核对?/span>Q用计Cؓ1Q。和q程内核对象一PU程内核对象也是一个小型数据结构,操作pȝ用它来管理这个线E。这个主U程一开始就会执行由链接器设为应用程序入口的C/C++q行时启动例E,q最l调用你的WinMainQwWinMainQmain或wmain函数?br />Q?Q、如果系l成功创Z新进E和ȝE,CreateProcess返回TRUE?br />创徏子进E后Q?/span>
创徏一个进E内核对象时Q系l会为此对象分配一个独一无二的标识符Q系l中没有别的q程内核对象会有相同的ID~号
6、如何终止线E?br />关闭C个进E或U程的句柄,不会pȝ杀Lq程或线E。关闭句柄只是告诉系l你对进E或U程的统计数?br />不再感兴了。进E或U程会l执行,直至自行l止。(计数Q重Ҏ计数Q?br />q程可以通过以下4U方式终止:
Q?Q、主U程的入口函数返回(强烈推荐的方式)?br /> 让主U程的入口函数返回,可以保证发生以下几g事情Q?br /> 该线E创建的MC++对象都将p些对象的析构函数正确销毁?br /> 操作pȝ正释攄E堆栈用的内存?br /> pȝ进E的退Z码(在进E内核对象中l护Q设Z的入口函数的q回倹{?br /> pȝ递减q程内核对象的用计数?/span>
Q?Q、进E中的一个线E调用ExitProcess函数Q要避免q个方式Q?br />q程会在该进E中的一个线E调用ExitProcess函数时终止:
VOID ExitProcess(UINT fuExitCode);
一旦你的应用程序的ȝE从它的入口函数q回Q那么不当前在q程中是否正在运行其他线E,都会调用ExitProcess来终止进E。不q,如果在入口函C调用ExitThreadQ而不是调用ExitProcess或者简单地q回Q应用程序的ȝE将停止执行Q但只要q程中还有其他线E正在运行,q程׃会终止?br />
Q?Q、另一个进E中的线E调用TerminateProcess函数Q要避免q个方式Q?br />调用TerminateProcess也可以终止一个进E,但是q程无法它在内存中的Q何信息{储到盘上?/span>
Q?Q、进E中的所有线E都“自然M”Q这是很隑֏生的Q?/span>
]]>
使用计数是所有内核对象类型都有的一个数据成员。初ơ创Z个对象的时候,其用计数被设ؓ1。另一个进E获得对现有内核对象的访问后Q用计数就会递增。进E终止运行后Q内核将自动递减此进E仍然打开的所有内核对象的使用计数。一个对象的使用计数变成0Q内核就会销毁该对象。这样一来,可以保证pȝ中不存在没有被Q何进E引用的内核对象?br />
3、管理内核对?br />一个进E在初始化时Q系l将为它分配一个句柄表。一个进E的句柄表,它只是一个由数据l构l成的数l。每个结?br />都包含指向一个内核对象的指针、一个访问掩码(access maskQ和一些标志?br />Q?Q、创Z个内核对?br />一个进E首ơ初始化的时候,其句柄表为空。当q程内的一个线E调用一个会创徏内核对象的函数时Q内核将个对象分配ƈ初始化一个内存块。然后,内核扫描q程的句柄表Q查找一个空白的记录(empty entryQ。指针成员会被设|成内核对象的数据结构的内部内存地址Q访问掩码将被设|成拥有完全讉K权限Q标志也会设|?br />
如果创徏调用p|Q那么返回的句柄值通常?QNULLQ。之所以失败,可能是由于系l内存不I或者遇C一个安全问题。遗憄是,有几个函数在调用p|时会q回句柄?#8211;1。所以要看清楚再判断?br />Q?Q、关闭内存对?br />无论以什么方式创建内核对象,都要调用CloseHandle向系l指Z已经l束使用对象Q?br />BOOL CloseHandle(HANDLE hobject);
l束旉要注意:Q;
①、在内部Q该函数首先查主调进E的句柄表,验证“传给函数的句柄?#8221;标识的是“q程实有权讉K的一个对?#8221;。如果句柄是有效的,pȝ将获得内核对象的数据结构的地址Qƈ在结构中递减“使用计数”成员。如果用计数变?Q内核对象将被销毁,q从内存中删除?br /> ②、一旦调用CloseHandleQ你的进E就不能讉K那个内核对象?br /> ③、如果对象的使用计数没有递减?Q它׃会被销毁。它表明另外q有一个或多个q程在用该对象。当其他q程全部停止使用q个对象后(通过调用CloseHandleQ,对象׃被销毁?br /> ④、在创徏一个内核对象时Q我们会它的句柄保存到一个变量中。将此变量作为参数调用了CloseHandle函数后,q应同时这个变量设为NULL?br /> ⑤、当q程l止q行Q操作系l会保此进E所使用的所有资源都被释放!对于内核对象Q操作系l执行的是以下操作:q程l止Ӟpȝ自动扫描该进E的句柄表。如果这个表中有M有效的记录项Q即q程l止前没有关闭的对象Q,操作pȝ会ؓ你关闭这些对象句柄。Q何这些对象的使用计数递减?Q内核就会销毁对象?br />
Q?Q、进E共享内核对象?br />很多地方需要用到进E共享内核对象。例?br />①、利用文件映对象,可以在同一台机器上q行的两个不同进E?span style="color: red">之间?/span>享数据块?nbsp;
②、借助mailslotsQ信)和named pipesQ匿名管道)Q在|络中的不同计算Zq行的进E可以相互发送数据块?br />③、mutexesQ互斥对象)、semaphoresQ信标对象)和事件允怸同进E中的线E同步执行。例如,一个应用程序可能需要在完成某个d之后Q向另一个应用程序发出通知?br />
三种不同的机制来允许q程׃n内核对象Q?/strong>使用对象句柄l承Qؓ对象命名Q以及复制对象句柄?/strong>
1 使用对象句柄l承
讄可承标志ؓ1。对象句柄的l承只会在生成子q程的时候发生?br />E序初始化时会ؓ父进E创Z个进E句柄表Q用对象句柄承,pȝ会ؓ子进E创Z个新的、空白的q程句柄?#8212;—像它ؓM一个新q程所做的那样。系l会遍历父进E的句柄表,对它的每一个记录项q行查。凡是包含一个有效的“可承的句柄”的项Q都会被完整地拷贝到子进E的句柄表。在子进E的句柄表中Q拷贝项的位|与它在父进E句柄表中的位置是完全一L。这是非帔R要的一个设计,因ؓ它意味着Q在父进E和子进E中Q对一个内核对象进行标识的句柄值是完全一L。内核对象的使用计数递增?br />2、改变句柄的标志
父进E创Z一个内核对象,得到了一个可l承的句柄,然后生成了两个子q程。但是,父进E只希望其中的一个子q程l承内核对象句柄。调用SetHandleInformation函数来改变内核对象句柄的l承标志。函数具体参考MSDN?nbsp;
3、ؓ对象命名
不能创徏相同名字的对象,cd不同也不行。进E间׃nQ?br />q程A 有某个命?JeffMutex" 对象 hMutexProcessA,那么q程B 创徏一个同?JeffMutex" 对象时。,
pȝ首先会查看是否存在一个名?#8220;JeffMutex”的内核对象。由于确实存在这L一个对象,所以内核接着查对象的cd。由于试囑ֈZ个mutexQ而名?#8220;JeffMutex”的对象也是一个mutexQ所以系l接着执行一ơ安全检查,验证调用者是否拥有对象的完全讉K权限。如果答案是肯定的,pȝ׃在Process B的句柄表中查找一个空白记录项Qƈ其初始化ؓ指向现有的内核对象。如果对象的cd不匹配,或调用者被拒绝讉KQCreateMutex׃p|Q返回NULLQ?br />q样实Cq程׃n对象。但问题是进EB不知道自己创建的是新对象Q还是用久对象?br />Q?Q、复制对象句?br />Z跨越q程边界来共享内核对象,最后一个技术是使用DuplicateHandle函数?br />q个函数获得一个进E的句柄表中的一个记录项Q然后在另一个进E的句柄表中创徏q个记录的一个拷贝?br />
]]>
lg特点Q?/span>
① 、可修改Q可替换Q满用L需?/span>
② 、有lg库可以快速组装,AcitveX控g
③ 、分布式Q事务逻辑和系l服务相分离,化了pȝ开发的复杂性,
④ 、组仉要动态连接,Q不可能在运行时~译?/span>
⑤ 、要装Q不要改变其接口?/span>
⑥ 、以二进制的形式发布Q已~译好的?/span>
2?span style="font: 7pt 'Times New Roman'"> 接口Q提供两个不同对象间的一U连接,计算机是通过一l函数连接v来的Q其实这l函数实质上是定义了程序中不同部分的接口。对?/span>COM接口是包含一个函数指针数l的内存l构?/span>
用纯虚函C为接口。看下面l典例子Q?/span>
#define InSruct struct
InSruct IA //定义接口
{
virtual void Fun1()=0;
};
class CA:public IA //定义lgQ注意类可以l承l构体,l构体也能承结构体Q?/span>
{
void Fun1()
{
cout<<"class Fun1()";
}
};
Void main()
{
CA *pCa=new CA;
IA *pIa=pCa;
pIa->Fun1(); //表示接口Q在E序内部实现?/span>
delete pCa;
}
//特别注意的是Q类不是lgQ组件不一定要cd玎ͼ也不不一定需要ѝ?/span>
3?span style="font: 7pt 'Times New Roman'"> lg的内存(探讨COM接口Z么可以用U抽象基cd?/span>COMlgQ?/span>
抽象基类提供vtbl指针指向虚拟函数、每个对象共享同一?/span>vtbl指针Q但数据各自不同
//////////////////////////////////////////////////////////////////////////////////////////////
4?span style="font: 7pt 'Times New Roman'"> 客户通过不断询问清晰lgq什么,但没有完成知道组件内部怎么栗?/span>
客户如何向组件询问关于它所支持的接口?lg如何回答、以及这U请求应{方式的l果?/span>
① ?/span>HRESULT QueryInterface( REFIID iid, void** ppvObject);函数查询某个lg是否支持某个特定的接?/span>
//iid 是要查询的接?/span>ID, ppvObject是返回的接口的指针,
② 关于cd转换Q?/span>
static_cast
用法Qstatic_cast < type-id > ( expression )
该运符把expression转换为type-idcdQ但没有q行时类型检查来保证转换的安全性。它主要有如下几U用法:
①用于cdơ结构中基类Q父c)?a target="_blank">zc?/span>Q子c)之间指针或引用的转换?
q行上行转换Q把zcȝ指针或引用{换成基类表示Q是安全的;
q行下行转换Q把基类指针或引用{换成zc表C)Ӟ׃没有动态类型检查,所以是不安全的?
②用于基本数据cd之间的{换,如把int转换成charQ把int转换成enum。这U{换的安全性也要开发h员来保证?
③把空指针转换成目标类型的I指针?
④把Q何类型的表达式{换成voidcd?
注意Qstatic_cast不能转换掉expression的const、volitale、或者__unaligned属性?
通常我们做的E序不应该只局限于E序间的操作。也许能对电脑上其他的程序操作会更有意思。我们能做到Q也很容易做到、?/p>
HWND FindWindow( LPCTSTR lpClassName, // pointer to class name LPCTSTR lpWindowName // pointer to window name );
推荐使用HWND FindWindowEx(
HWND hwndParent,// handle to parent window
HWND hwndChildAfter, // handle to a child window
LPCTSTR lpszClass, // pointer to class name
LPCTSTR lpszWindow // pointer to window name ); 功能更加强大?/strong>
FindWindowEx 能根据多U条件查找?/strong>
推荐使用工?br>
能快速查扑և你电脑的应用E序的类名与标题?/p>