Windows系统编程之进程间通信
Windows系统编程之进程间通信
作者:北极星2003
来源:看雪论坛(www.pediy.com)
Windows 的IPC(进程间通信)机制主要是异步管道和命名管道。(至于其他的IPC方式,例如内存映射、邮槽等这里就不介绍了)
管道(pipe)是用于进程间通信的共享内存区域。创建管道的进程称为管道服务器,而连接到这个管道的进程称为管道客户端。一个进程向管道写入信息,而另外一个进程从管道读取信息。
异步管道是基于字符和半双工的(即单向),一般用于程序输入输出的重定向;命名管道则强大地多,它们是面向消息和全双工的,同时还允许网络通信,用于创建客户端/服务器系统。
一、异步管道(实现比较简单,直接通过实例来讲解)
实验目标:当前有sample.cpp, sample.exe, sample.in这三个文件,sample.exe为sample.cpp的执行程序,sample.cpp只是一个简单的程序示例(简单求和),如下:
- #include <iostream.h>
- int main()
- {
- int a, b ;
- while ( cin >> a >> b && ( a || b ) )
- cout << a + b << endl ;
- return 0;
- }
32 433
542 657
0 0
要求根据sample.exe和它的输入数据,把输出数据重定向到sample.out
流程分析:实际这个实验中包含两个部分,把输入数据重定向到sample.exe 和把输出数据重定向到sample.out。在命令行下可以很简单的实现这个功能“sample <sample.in >sample.out”,这个命令也是利用管道特性实现的,现在我们就根据异步管道的实现原理自己来实现这个功能。
管道是基于半双工(单向)的,这里有两个重定向的过程,显然需要创建两个管道,下面给出流程图:
1)。父进程是我们需要实现的,其中需要创建管道A,管道B,和子进程,整个实现流程分为4个操作。
2)。管道A:输入管道
3)。管道B:输出管道
4)。操作A:把输入文件sample.in的数据写入输入管道(管道A)
5)。操作B:子进程从输入管道中读取数据,作为该进程的加工原料。通常,程序的输入数据由标准的输入设备输入,这里实现输入重定向,即把输入管道作为输入设备。
6)。操作C:子进程把加工后的成品(输出数据)输出到输出管道。通常,程序的输出数据会输出到标准的输出设备,一般为屏幕,这里实现输出重定向,即把输出管道作为输出设备。
7)。操作D:把输出管道的数据写入输出文件
需要注意的是,管道的本质只是一个共享的内存区域。这个实验中,管道区域处于父进程的地址空间中,父进程的作用是提供环境和资源,并协调子进程进行加工。
程序源码:
- #include <windows.h>
- #include <iostream.h>
- const int BUFSIZE = 4096 ;
- HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
- hChildStdoutRd,hChildStdoutWr,hChildStdoutRdDup,
- hSaveStdin, hSaveStdout;
- BOOL CreateChildProcess(LPTSTR);
- VOID WriteToPipe(LPTSTR);
- VOID ReadFromPipe(LPTSTR);
- VOID ErrorExit(LPTSTR);
- VOID ErrMsg(LPTSTR, BOOL);
- void main( int argc, char *argv[] )
- {
- // 处理输入参数
- if ( argc != 4 )
- return ;
- // 分别用来保存命令行,输入文件名(CPP/C),输出文件名(保存编译信息)
- LPTSTR lpProgram = new char[ strlen(argv[1]) ] ;
- strcpy ( lpProgram, argv[1] ) ;
- LPTSTR lpInputFile = new char[ strlen(argv[2]) ];
- strcpy ( lpInputFile, argv[2] ) ;
- LPTSTR lpOutputFile = new char[ strlen(argv[3]) ] ;
- strcpy ( lpOutputFile, argv[3] ) ;
- SECURITY_ATTRIBUTES saAttr;
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- saAttr.lpSecurityDescriptor = NULL;
- /************************************************
- * redirecting child process's STDOUT *
- ************************************************/
- hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
- if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
- ErrorExit("Stdout pipe creation failed/n");
- if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
- ErrorExit("Redirecting STDOUT failed");
- BOOL fSuccess = DuplicateHandle(
- GetCurrentProcess(),
- hChildStdoutRd,
- GetCurrentProcess(),
- &hChildStdoutRdDup ,
- 0,
- FALSE,
- DUPLICATE_SAME_ACCESS);
- if( !fSuccess )
- ErrorExit("DuplicateHandle failed");
- CloseHandle(hChildStdoutRd);
- /************************************************
- * redirecting child process's STDIN *
- ************************************************/
- hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
- if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
- ErrorExit("Stdin pipe creation failed/n");
- if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
- ErrorExit("Redirecting Stdin failed");
- fSuccess = DuplicateHandle(
- GetCurrentProcess(),
- hChildStdinWr,
- GetCurrentProcess(),
- &hChildStdinWrDup,
- 0,
- FALSE,
- DUPLICATE_SAME_ACCESS);
- if (! fSuccess)
- ErrorExit("DuplicateHandle failed");
- CloseHandle(hChildStdinWr);
- /************************************************
- * 创建子进程(即启动SAMPLE.EXE) *
- ************************************************/
- fSuccess = CreateChildProcess( lpProgram );
- if ( !fSuccess )
- ErrorExit("Create process failed");
- // 父进程输入输出流的还原设置
- if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))
- ErrorExit("Re-redirecting Stdin failed/n");
- if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))
- ErrorExit("Re-redirecting Stdout failed/n");
- WriteToPipe( lpInputFile ) ;
- ReadFromPipe( lpOutputFile );
- delete lpProgram ;
- delete lpInputFile ;
- delete lpOutputFile ;
- }
- BOOL CreateChildProcess( LPTSTR lpProgram )
- {
- PROCESS_INFORMATION piProcInfo;
- STARTUPINFO siStartInfo;
- BOOL bFuncRetn = FALSE;
- ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
- ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
- siStartInfo.cb = sizeof(STARTUPINFO);
- bFuncRetn = CreateProcess ( NULL, lpProgram, NULL, NULL, TRUE, /
- 0, NULL, NULL, &siStartInfo, &piProcInfo);
- if (bFuncRetn == 0)
- {
- ErrorExit("CreateProcess failed/n");
- return 0;
- }
- else
- {
- CloseHandle(piProcInfo.hProcess);
- CloseHandle(piProcInfo.hThread);
- return bFuncRetn;
- }
- }
- VOID WriteToPipe( LPTSTR lpInputFile )
- {
- HANDLE hInputFile = CreateFile(lpInputFile, GENERIC_READ, 0, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
- if (hInputFile == INVALID_HANDLE_VALUE)
- return ;
- BOOL fSuccess ;
- DWORD dwRead, dwWritten;
- CHAR chBuf[BUFSIZE] = {0} ;
- for (;;)
- {
- fSuccess = ReadFile( hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ;
- if ( !fSuccess || dwRead == 0)
- break;
- fSuccess = WriteFile( hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL) ;
- if ( !fSuccess )
- break;
- }
- if (! CloseHandle(hChildStdinWrDup))
- ErrorExit("Close pipe failed/n");
- CloseHandle ( hInputFile ) ;
- }
- VOID ReadFromPipe( LPTSTR lpOutputFile )
- {
- HANDLE hOutputFile = CreateFile( lpOutputFile, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if (hOutputFile == INVALID_HANDLE_VALUE)
- return ;
- BOOL fSuccess ;
- DWORD dwRead, dwWritten;
- CHAR chBuf[BUFSIZE] = { 0 };
- if (!CloseHandle(hChildStdoutWr))
- ErrorExit("Closing handle failed");
- for (;;)
- {
- fSuccess = ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL) ;
- if( !fSuccess || dwRead == 0)
- {
- break;
- }
- fSuccess = WriteFile( hOutputFile, chBuf, dwRead, &dwWritten, NULL) ;
- if ( !fSuccess )
- break;
- }
- CloseHandle ( hOutputFile ) ;
- }
- VOID ErrorExit (LPTSTR lpszMessage)
- {
- MessageBox( 0, lpszMessage, 0, 0 );
- }
命名管道具有以下几个特征:
(1)命名管道是双向的,所以两个进程可以通过同一管道进行交互。
(2)命名管道不但可以面向字节流,还可以面向消息,所以读取进程可以读取写进程发送的不同长度的消息。
(3)多个独立的管道实例可以用一个名称来命名。例如几个客户端可以使用名称相同的管道与同一个服务器进行并发通信。
(4)命名管道可以用于网络间两个进程的通信,而其实现的过程与本地进程通信完全一致。
实验目标:在客户端输入数据a和b,然后发送到服务器并计算a+b,然后把计算结果发送到客户端。可以多个客户端与同一个服务器并行通信。
界面设计:
实现的过程比较简单,但有一个难点。原本当服务端使用ConnectNamedPipe函数后,如果有客户端连接,就可以直接进行交互。原来我在实现过程中,当管道空闲时,管道的线程函数会无限(INFINITE)阻塞。若现在需要停止服务,就必须结束所有的线程,TernimateThread可以作为一个结束线程的方法,但我基本不用这个函数。一旦使用这个函数之后,目标线程就会立即结束,但如果此时的目标线程正在操作互斥资源、内核调用、或者是操作共享DLL的全局变量,可能会出现互斥资源无法释放、内核异常等现象。这里我用重叠I/0来解决这个问题,在创建PIPE时使用FILE_FLAG_OVERLAPPED标志,这样使用ConnectNamedPipe后会立即返回,但线程的阻塞由等待函数WaitForSingleObject来实现,等待OVERLAPPED结构的事件对象被设置。
客户端主要代码:
- void CMyDlg::OnSubmit()
- {
- // 打开管道
- HANDLE hPipe = CreateFile("////.//Pipe//NamedPipe", GENERIC_READ | GENERIC_WRITE, /
- 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) ;
- if ( hPipe == INVALID_HANDLE_VALUE )
- {
- this->MessageBox ( "打开管道失败,服务器尚未启动,或者客户端数量过多" ) ;
- return ;
- }
- DWORD nReadByte, nWriteByte ;
- char szBuf[1024] = {0} ;
- // 把两个整数(a,b)格式化为字符串
- sprintf ( szBuf, "%d %d", this->nFirst, this->nSecond ) ;
- // 把数据写入管道
- WriteFile ( hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;
- memset ( szBuf, 0, sizeof(szBuf) ) ;
- // 读取服务器的反馈信息
- ReadFile ( hPipe, szBuf, 1024, &nReadByte, NULL ) ;
- // 把返回信息格式化为整数
- sscanf ( szBuf, "%d", &(this->nResValue) ) ;
- this->UpdateData ( false ) ;
- CloseHandle ( hPipe ) ;
- }
- // 启动服务
- void CMyDlg::OnStart()
- {
- CString lpPipeName = "////.//Pipe//NamedPipe" ;
- for ( UINT i = 0; i < nMaxConn; i++ )
- {
- // 创建管道实例
- PipeInst[i].hPipe = CreateNamedPipe ( lpPipeName, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, /
- PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, nMaxConn, 0, 0, 1000, NULL ) ;
- if ( PipeInst[i].hPipe == INVALID_HANDLE_VALUE )
- {
- DWORD dwErrorCode = GetLastError () ;
- this->MessageBox ( "创建管道错误!" ) ;
- return ;
- }
- // 为每个管道实例创建一个事件对象,用于实现重叠IO
- PipeInst[i].hEvent = CreateEvent ( NULL, false, false, false ) ;
- // 为每个管道实例分配一个线程,用于响应客户端的请求
- PipeInst[i].hTread = AfxBeginThread ( ServerThread, &PipeInst[i], THREAD_PRIORITY_NORMAL ) ;
- }
- this->SetWindowText ( "命名管道实例之服务器(运行)" ) ;
- this->MessageBox ( "服务启动成功" ) ;
- }
- // 停止服务
- void CMyDlg::OnStop()
- {
- DWORD dwNewMode = PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_NOWAIT ;
- for ( UINT i = 0; i < nMaxConn; i++ )
- {
- SetEvent ( PipeInst[i].hEvent ) ;
- CloseHandle ( PipeInst[i].hTread ) ;
- CloseHandle ( PipeInst[i].hPipe ) ;
- }
- this->SetWindowText ( "命名管道实例之服务器" ) ;
- this->MessageBox ( "停止启动成功" ) ;
- }
- // 线程服务函数
- UINT ServerThread ( LPVOID lpParameter )
- {
- DWORD nReadByte = 0, nWriteByte = 0, dwByte = 0 ;
- char szBuf[MAX_BUFFER_SIZE] = {0} ;
- PIPE_INSTRUCT CurPipeInst = *(PIPE_INSTRUCT*)lpParameter ;
- OVERLAPPED OverLapStruct = { 0, 0, 0, 0, CurPipeInst.hEvent } ;
- while ( true )
- {
- memset ( szBuf, 0, sizeof(szBuf) ) ;
- // 命名管道的连接函数,等待客户端的连接(只针对NT)
- ConnectNamedPipe ( CurPipeInst.hPipe, &OverLapStruct ) ;
- // 实现重叠I/0,等待OVERLAPPED结构的事件对象
- WaitForSingleObject ( CurPipeInst.hEvent, INFINITE ) ;
- // 检测I/0是否已经完成,如果未完成,意味着该事件对象是人工设置,即服务需要停止
- if ( !GetOverlappedResult ( CurPipeInst.hPipe, &OverLapStruct, &dwByte, true ) )
- break ;
- // 从管道中读取客户端的请求信息
- if ( !ReadFile ( CurPipeInst.hPipe, szBuf, MAX_BUFFER_SIZE, &nReadByte, NULL ) )
- {
- MessageBox ( 0, "读取管道错误!", 0, 0 ) ;
- break ;
- }
- int a, b ;
- sscanf ( szBuf, "%d %d", &a, &b ) ;
- pMyDlg->nFirst = a ;
- pMyDlg->nSecond = b ;
- pMyDlg->nResValue = a + b ;
- memset ( szBuf, 0, sizeof(szBuf) ) ;
- sprintf ( szBuf, "%d", pMyDlg->nResValue ) ;
- // 把反馈信息写入管道
- WriteFile ( CurPipeInst.hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;
- pMyDlg->SetDlgItemInt ( IDC_FIRST, a, true ) ;
- pMyDlg->SetDlgItemInt ( IDC_SECOND, b, true ) ;
- pMyDlg->SetDlgItemInt ( IDC_RESULT, pMyDlg->nResValue, true ) ;
- // 断开客户端的连接,以便等待下一客户的到来
- DisconnectNamedPipe ( CurPipeInst.hPipe ) ;
- }
- return 0 ;
- }
Windows系统编程之进程间通信的更多相关文章
- Windows系统编程之异步I/O和完成端口
Windows系统编程之异步I/O和完成端口[作者]北极星2003[来源]看雪技术论坛(bbs.pediy.com) [时间]2006年7月1日 一. 同步I/O和异步I/O 在介绍这部分内容之前先 ...
- Windows系统编程之进程同步试验
试验过程中调用了不少系统函数,并且涉及到一些系统级的概念,在此记录下来做为解决问题的一种方式.也许在以后的编程的过程中是否可以通过调用系统平台上的东西来完成一些任务,这仍不失为一种好的思维方式. 多线 ...
- linux系统编程:进程间通信-mmap
进程间通信-mmap #include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, in ...
- linux系统编程:进程间通信-fifo
进程间通信-fifo 进程间通信的还有一种方式是fifo. fifo是还有一种管道:有名管道.从名字能够看出.它也是队列. 使用fifo通信前,得先创建fifo $ mkfifo myfifo 随后仅 ...
- 编程实现Windows系统自动登录
编程实现Windows系统自动登录 原理: 通过注册表修改实现.Windows内置了自动登录的机制,在登录系统时,winlogon会检查注册表下有没有设置自动登录,如果设置了就上就会读取用户名和密码, ...
- 【Windows核心编程】一个使用内存映射文件进行进程间通信的例子
进程间通信的方式有很多种,其底层原理使用的都是内存映射文件. 本文实现了Windows核心编程第五版475页上的demo,即使用内存映射文件来在进程间通信. 进程1 按钮[Create mappin ...
- Linux系统编程——进程间通信:管道(pipe)
管道的概述 管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,全部的 UNIX 系统都支持这样的通信机制. 无名管道有例如以下特点: 1.半双工,数据在同一时刻仅仅能在一个 ...
- Python 编程环境搭建(Windows 系统中)
由于大家普遍使用 Windows 系统,所以本文只介绍 Windows 系统中 Python 环境的安装. 在 Windows 中安装 Python 与安装普通软件没什么差别,下载所需版本的安装包后, ...
- 《Python黑帽子:黑客与渗透测试编程之道》 Windows系统提权
环境准备: pywin32的安装在第八章的键盘记录中有,这里还需要安装wmi: 在本人的32位win7上本来是没有easy_install这个命令的,这需要安装setuptools-0.6c11.wi ...
随机推荐
- OpenMP并行程序设计
1.fork/join并行执行模式的概念 2.OpenMP指令和库函数介绍 3.parallel 指令的用法 4.for指令的使用方法 5 sections和section指令的用法 1.fork/j ...
- 【LeetCode】Swap Nodes in Pairs
Given a linked list, swap every two adjacent nodes and return its head. For example, Given 1->2-& ...
- qt 关于内存泄漏的检测
Qt 关于内存泄露的检测: 收藏人:guitarhua 2012-02-10 | 阅: 转: | 来源 | 分享 Qt 关于内存泄露的检测:工具篇 ...
- 【算法】最长公共子序列(nlogn)
转载注明出处:http://blog.csdn.net/wdq347/article/details/9001005 (修正了一些错误,并自己重写了代码) 最长公共子序列(LCS)最常见的算法是时间复 ...
- codeforces 251A Points on Line(二分or单调队列)
Description Little Petya likes points a lot. Recently his mom has presented him n points lying on th ...
- android EditText设置光标、边框和图标
控制边框形状,先在drawable中建一个xml文件:shape.xml <?xml version="1.0" encoding="utf-8"?> ...
- (转)Thinkphp系统常量 演示
Thinkphp2.1框架内置了许多系统常量, 具体如下: __ROOT__ : 网站根目录地址 __APP__ : 当前项目(入口文件)地址 __URL__ : 当前模块地址 __ACTION__ ...
- c#字符串驻留机制
http://www.cnblogs.com/instance/archive/2011/05/24/2056091.html
- 安装PHP过程中,make步骤报错:(集合网络上各种解决方法)
安装PHP过程中,make步骤报错:(集合网络上各种解决方法) (1)-liconv -o sapi/fpm/php-fpm /usr/bin/ld: cannot find -liconv coll ...
- word2vec生成词向量原理
假设每个词对应一个词向量,假设: 1)两个词的相似度正比于对应词向量的乘积.即:$sim(v_1,v_2)=v_1\cdot v_2$.即点乘原则: 2)多个词$v_1\sim v_n$组成的一个上下 ...