windows进程通信之开篇

本文由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 here
	SHELLEXECUTEINFO 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 here
	SECURITY_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天酒店

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值