windows进程通信之开篇

来源:互联网 发布:阿里云服务器esc 编辑:程序博客网 时间:2024/06/18 14:48

本文由danny发表于 http://blog.csdn.net/danny_share

 

本篇写的比较随性,有点洋洋洒洒,望见谅。

既然要聊进程间通信,首先得理一些概念,

1.      进程相关概念

(1)程序:

在Windows下,PE文件前两个字节是十六进制的4D 5A(PE文件详细头信息参考http://blog.csdn.net/qiming_zhang/article/details/7309909)

将exe文件载入WinHex,可看到文件前两个字节就是MZ(另外,比如医疗行业的dicom文件,其实也就是一种具备特殊格式的文件:有128字节的导言跟着四个字节的DICM。。。)

一切文件都可看做是特殊格式的字符串,从而任何文件我们都可以看做是txt文件,包括exe(这点跟各种数据类型比如int,string都可理解成一个个的char有异曲同工之妙),只要我们将其文件头看做是普通的文本。


(2)进程

【a】对于DOS,进程就是一个运行着的程序

【b】对于Windows,进程是资源分配的基本单位,是线程运行的容器


(3)线程

Windows95开始支持多线程技术以便实现类似于“边听歌边写程序”的需求。

刚接触进程和线程时,一直困惑既然有了进程,为何还要设计线程,比如要实现“边听歌边写程序”,则均分CPU给这两个进程不就行了吗。后细想,如何能够在一个exe里同时实现这两个功能呢,除非运行一个能够实现写程序的A.exe,A.exe运行以后默默的在后台打开能够听歌的B.exe。这还好办,假如要A.exe在编译的同时还能搜索函数名,好,A.exe又要打开能实现搜索函数名的C.exe,且A.exe和C.exe要共享所有的函数名。A.exe在编译和搜索函数名的同时还要实现阅读代码,好了,又来D.exe,又要共享数据。。。

这里,我们发现这么一个基础设施,就是最好有一个独立于进程本身的运行概念,以便CPU独立于进程来管理运行单元,且同一个进程的多个运行单元最好能够共享内存,于是聪明的IT先辈们设计出了线程。

进程于是变成了分配内存等非CPU运行资源的线程运行容器,

而线程则成了真正干活的“运行单元”


(3)多核

     刚谈到为实现“边听歌边写程序”的需求,CPU可以通过分时实现,但反过来一想,CPU之所以分时,是因为只有一个没法分身啊,要是有两个就好了,2005年Intel的PentiumD处理器正式拉开了多核处理器时代的大幕(详见CPU历史http://jettcai.blog.51cto.com/1447637/845866),当线程数量不超过CPU数量时,计算机理论上可实现真正的同时。


(4)多处理器

一个电脑上装有多个CPU(当然每个CPU又可以有多个核),有种分布式的感觉。

记得上学时做过一个风电项目,计算风力发电机叶片和旋转轮毂的载荷计算。数据量太大,1台普通双核电脑要跑几天才能算完,后改用IBM刀片机,8核32线程,4个小时就算完了。


(5)超线程

本来一颗CPU同时只能运行一个线程,通过超线程技术从逻辑上分成两个或多个,使之可以运行多个线程


(6)超频

提高CPU主频,同样的时间可以跑更多指令

 

2.进程的生命周期(这话很有Java的味道啊)

2,1出生

   2.2.1出生之函数篇

  Windows平台下打开外部文件有多个函数

ID

函数

 

特点

1

system

同步

标准C语言库函数,调用时会有DOS窗口

2

ShellExecute

异步

比较适合打开网站、打开默认邮件、打开外部图片等

3

ShellExecuteEx

异步

可以返回新创建进程的句柄

适合打开外部程序,并要等它执行结束的情况

4

WinExec

异步

老函数了,只能打开可执行文件,但很简单明了

5

CreateProcess

同步

适合需要和新建进程交互的情况,用法复杂

   

(1)      system函数

system("C:\\Windows\\system32\\NotePad.exe");

(2)      WinExec函数

UINT result= WinExec("C:\\Windows\\system32\\NotePad.exe",SW_SHOWMAXIMIZED);switch(result){case 0:{}break;case ERROR_BAD_FORMAT:{//WinExec只能运行可执行文件,不像ShellExecute和ShellExecuteEx可打开文件        //如果WinExec打开文件,如D:\\InstallInfo.txt,则引发此错误}break;case ERROR_FILE_NOT_FOUND:{}break;case ERROR_PATH_NOT_FOUND:{}break;}

(3)      ShellExecute函数

<pre name="code" class="cpp">static const int ShellExecute_SUCCESS_CODE=32;// TODO: Add your control notification handler code here//(1)此时记事本始终是最大化显示的//(2)应用实例:例如,Varian的加速器RapidArc配备的OBI开机时会自启动一个文本来显示安装信息//(3)最后一个参数表示显示方式,值在0-11之间,这里设置成最大化//(4)异步执行HINSTANCE myInstance=ShellExecute( AfxGetMainWnd()->m_hWnd, "open", "C:\\Windows\\system32\\NotePad.exe", "D:\\InstallInfo.txt",NULL,SW_SHOWMAXIMIZED);int result=(int)myInstance;if(ShellExecute_SUCCESS_CODE<result){//success//(1)假如把SW_SHOWMAXIMIZED换成100(正常在0-11之间)//   虽返回值仍大于32,但此时我们却没有看见notepad//   是因为打开了notepad.exe进程,但系统无法正确显示它//   导致任务管理器有notepad.exe进程用户却看不见//(2)假如外部MFC程序在OnInitDialog()里ShowWindow(SW_MINIMIZE);了,ShellExecute再设置成SW_SHOWMAXIMIZED,则认外部程序自己的设置}else{switch(result){case 0:{//内存不足或者资源不足}break;case ERROR_FILE_NOT_FOUND:{//文件不存在,//比如把NotePad.exe改成note.exe,//但若只把本地文件"D:\\InstallInfo.txt"删除,不会引发本错误,因为此时"D:\\InstallInfo.txt"只是“NotePad.exe”的参数}break;case ERROR_PATH_NOT_FOUND:{//路径不存在,//但实际上,将参数open改为open1,会返回这个错误}break;case ERROR_BAD_FORMAT:{//exe格式不对,比如打开exe后缀文件时,该文件不是标准exe文件//但实际上我将一个纯文本文件后后缀名改成exe后,再用ShellExecute打开它,返回的是SE_ERR_ACCESSDENIED}break;case SE_ERR_ACCESSDENIED:{//无权访问//这里我把C盘改成本机不存在的M盘时,会引发本错误}break;case SE_ERR_ASSOCINCOMPLETE:{//文件名不符合Windows规范}break;case SE_ERR_DDEBUSY:{//DDE繁忙}break;case SE_ERR_DDEFAIL:{//DDE事务失败}break;case SE_ERR_DDETIMEOUT:{//DDE事务超时}break;case SE_ERR_DLLNOTFOUND:{//DLL不存在}break;//case SE_ERR_FNF:  //注意:此值和ERROR_FILE_NOT_FOUND一样//{//没有找到文件  估计FNF是File Not Found的缩写//}//break;case SE_ERR_NOASSOC:{//无相关应用程序能够打开该文件}break;case SE_ERR_OOM:{//内存不足,无法打开该文件}break;//case SE_ERR_PNF: //注意:此值和ERROR_PATH_NOT_FOUND一样//{//}//break;case SE_ERR_SHARE:{}break;}}


(4)      ShellExecuteEx函数

// TODO: Add your control notification handler code hereSHELLEXECUTEINFO myshell;                  memset(&myshell, 0, sizeof(myshell));     myshell.cbSize = sizeof(myshell);     myshell.hwnd = NULL;     myshell.lpVerb = _T("open");     //myshell.lpFile = "C:\\Windows\\system32\\NotePad.exe"; // myshell.lpParameters="D:\\InstallInfo.txt" ;   myshell.lpFile = "D:\\InstallInfo.txt"; //可以直接写文件名,不一定是可执行文件,ShellExecute也是一样 myshell.nShow = SW_SHOWNORMAL;     myshell.fMask = SEE_MASK_NOCLOSEPROCESS;      BOOL bResult = ShellExecuteEx(&myshell); DWORD result=WaitForSingleObject(myshell.hProcess,INFINITE);   if(WAIT_OBJECT_0 ==result) { //此时只有新打开的记事本进程关闭的时候,才会结束 }




(5)      CreateProcess函数

// TODO: Add your control notification handler code hereSECURITY_ATTRIBUTES saProcess;saProcess.nLength=sizeof(saProcess);saProcess.lpSecurityDescriptor=NULL;saProcess.bInheritHandle=TRUE;SECURITY_ATTRIBUTES saThread;saThread.nLength=sizeof(saThread);saThread.lpSecurityDescriptor=NULL;saThread.bInheritHandle=FALSE;PROCESS_INFORMATION piProcess;STARTUPINFO si={sizeof(si)};//可调整进程优先级if(TRUE==CreateProcess(NULL,"C:\\Windows\\system32\\NotePad.exe",&saProcess,&saThread,FALSE,ABOVE_NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&piProcess)){//}


2.2.2出生之过程篇

详见http://www.longene.org/techdoc/0625005001224576737.html

也可参见《深入浅出MFC第二版》中39页

【1】shell调用CreateProcess激活我们的程序

【2】系统产生一个核心对象,计数值为1

【3】对于32位操作系统,系统为此进程分配4GB的地址空间

【4】加载器加载相应资源

【5】系统建立该进程的一个主线程

【6】开始运行  

 

2.2.3出生之内存篇

进程作为给线程运行提供资源的平台,其中一项最重要的资源就是内存,这里仅简述不同平台所支持的内存大小,分别是DOS时代的16位,(之前以为DOS已被淘汰,但前阵子看到Varian的加速器的上位机软件就是DOS平台,瞬感医疗行业稳定压倒一切啊),win32位年代和x64年代(关于内存寻址有篇好玩的文章http://bbs.pediy.com/showthread.php?t=115101)。以后有机会的话整理一下内存方面的内容。

ID

平台

特点

1

DOS16位平台

(1)20跟地址线,16跟数据线

(2)最大1M寻址能力

2

Win32平台

(1)4G虚拟地址空间

3

X64平台

(1)理论支持16TB内存,实际64位Windows最大支持192G

 

(1)DOS16位平台

     VMware安装了MS-DOS7.1(VmWare安装DOS教程见http://blog.csdn.net/fengfengdiandia/article/details/7457803),然后使用UltraISO制作了TurboC的ISO镜像,再通过命令行从虚拟光驱中拷贝到DOS系统下。然后在调用turboC文件夹里的Install安装,最后启动C:\TC下的TC,即可打开TurboC啦,古老而经典的IDE啊。

运行以后发现变量a的地址是FFF4,即指针是16位的,

同时,实模式的实在体现于所访问的地址就是物理地址,不像保护模式下,经过操作系统的映射,进程访问的地址和物理地址不再一一对应

                                                                       

(2)win32年代

保护模式下,Windows给每个进程分配4G的虚拟地址空间。

地址较下的2G属于用户空间(比如这里的0x0015FD44就是较低地址的),较上的2G用于共享系统使用,当然也可以配置成用户空间为3G。

 

(3)X64平台

64位操作系统理论上支持2^64约16TB的内存。

 

2.2成长

拷贝一张来自http://oa.gdut.edu.cn/os/multimedia/oscai/chapter2/pages/ch22.htm的图片表示进程的各种状态

 

但实际上使用CPU资源的是线程,这故里不再赘述,有空的时候整理线程相关的内容

 

2.3死亡        

   进程的退出方式分成两种,一种是退出本进程的,另一种是退出非本进程

http://blog.csdn.net/claien/article/details/5796693

2.3.1退出本进程

ID

退出方式

备注

1

正常退出

即主线程正常结束以后,进程正常退出

 

2

exit

性质差不多

3

ExitProcess

4

TerminateProcess

5

发送WM_CLOSE消息

跟点击窗口上的关闭按钮是一个道理

 

6

PostThreadMessage

 

 

(1)正常退出

 

(2)exit、ExitProcess和TerminateProcess

exit对Console和窗口程序都适用

exit(0)


ExitProcess用于结束本进程

ExitProcess(0);

TerminateProcess主要用于结束其他进程

TerminateProcess(GetCurrentProcess(),0);

(5)发送WM_CLOSE消息

SendMessage(WM_CLOSE,0,0);

(6)PostThreadMessage

通过让线程发送WM_QUIT消息

PostThreadMessage(GetCurrentThreadId(),WM_QUIT,0,0);
 

2.3.2结束其他进程

ID

退出方式

备注

1

发送WM_CLOSE消息

 

2

TerminateProcess

 

 

(1)发送WM_CLOSE消息

首先获取目标窗口的句柄pCWnd,然后pCWnd发送WM_CLOSE消息

CWnd* pCWnd=FindWindow(NULL,"Test");    if(pCWnd!=NULL)    {       pCWnd->SendMessage(WM_CLOSE,0,0);    } 

(2)TerminateProcess

通过获取目标窗口句柄再转换成目标进程句柄,在TerminateProcess即可

HWND hWnd = ::FindWindow(NULL,"Test");DWORD myProcess=0;GetWindowThreadProcessId(hWnd,&myProcess); //注意:第二个参数是进程的ID,返回值是线程的ID。HANDLE hd =OpenProcess(PROCESS_ALL_ACCESS,FALSE, myProcess);TerminateProcess(hd, 0);


本文相关源代码下载地址

http://download.csdn.net/detail/danny_share/7680987


Danny

2014年7月26号

于天津河西7天酒店

0 0
原创粉丝点击