进程间通信的几种方式浅谈

网友投稿 1204 2022-10-03

进程间通信的几种方式浅谈

进程间通信的几种方式浅谈

程序员必须让拥有依赖关系的进程集协调,这样才能达到进程的共同目标。可以使用两种技术来达到协调。第一种技术在具有通信依赖关系的两个进程间传递信息。这种技术称做进程间通信(interprocess communication)。第二种技术是同步,当进程间相互具有合作依赖时使用。这两种类型的依赖关系可以同时存在。

进程间通信                                 描述

环境变量/文件描述符            子进程接受父进程环境数据的拷贝以及所有文件描述符。父进程可以在它的数据片断或环境中设置一定的变量,同时子进程接收这些值。父进程可以打开文件,同时推进读/写指针的位置,而且子进程使用相同的偏移访问该文件。

命令行参数                      在调用exec或派生函数期间,命令行参数可以传递给子进程。

管道                            用于相关和无关进程间的通信,而且形成两个进程间的一个通信通道,通常使用文件读写程序访问。

共享内存                        两个进程之外的内存块,两个进程均可以访问它。

DDE(动态数据交换,             使用客户机/服务器模型(C/S),服务器对客户的数据

Dynamic data exchange)         或动作请求作出反应。

一、环境变量、文件描述符:

当创建一个子进程时,它接受了父进程许多资源的拷贝。子进程接受了父进程的文本、堆栈

以及数据片断的拷贝。子进程也接受了父进程的环境数据以及所有文件描述符的拷贝。子进

程从父进程继承资源的过程创造了进程间通信的一个机会。父进程可以在它的数据片断或环

境中设置一定的变量,子进程于是接受这些值。同样,父进程也可以打开一个文件,推进到

文件内的期望位置,子进程接着就可以在父进程离开读/写指针的准确位置访问该文件。

这类通信的缺陷在于它是单向的、一次性的通信。也就是说,除了文件描述外,如果子进程

继承了任何其它数据,也仅仅是父进程拷贝的所有数据。 一旦创建了子进程,由子进程对

这些变量的任何改变都不会反映到父进程的数据中。同样,创建子进程后,对父进程数据的

任何改变也不会反映到子进程中。所以,这种类型的进程间通信更像指挥棒传递。一旦父进

程传递了某些资源的拷贝,子进程对它的使用就是独立的,必须使用原始传递资源。

二、命令行参数:

通过命令行参数(command-line argument)可以完成另一种单向、一次性的进程间通信

我前面的文章已经提到过使用命令行参数。命令行参数在调用一个exec或派生调用操作系

统时传递给子进程。命令行参数通常在其中一个参数中作为NULL终止字符串传递给exec

或派生函数调用。这些函数可以按单向、一次性方式给子进程传递值。WINDOWS有调用执行

exe程序的API。大家可以去参考一下ShellExecuteA函数相关。

三、管道通信:

继承资源以及命令行参数是最简单形式的进程间通信。它们同时有两个主要限制。除了文件

描述符外,继承资源是IPC的单向、一次性形式。传递命令参数也是单向、一次性的IPC

方法。这些方法也只有限制于关联进程,如果不关联,命令行参数和继承资源不能使用。还

有另一种结构,称做管道(Pipe),它可以用于在关联进程间以及无关联进程间进行通信。

管道是一种数据结构,像一个序列化文件一样访问。它形成了两个进程间的一种通信渠道。

管道结构通过使用文本和写方式来访问。如果进程A希望通过管道发送数据给进程B,那么

进程A向管道写入数据。为了让进程B接收此数据,进程B必须读取管道,与命令行参数的

IPC形式不一样。管道可以双向通信。两进程间的数据流是双向通信的。管道可以在程序的

整个执行期间使用,在进程间发送和接收数据。所以,管道充当可访问管道的进程间的一种

可活链接,有两种基本管道类型:

1.  匿名管道

2.  命名管道

上面的图可以看出在没有管道时,两进程是不能互写的。

建立管道后就可以相互通信了。

只有关联进程可以使用匿名管道来通信。无关联进程必须使用命名管道。

匿名管道:通过文件描述符或文件句柄提供对匿名管道的访问。对系统API的调用创建一个管道,并返回一个文件描述符。这个文件描述符是用作read()或write()函数的一个参数。当通过文件描述符调用read()或write()时,数据的源和目标就是管道。例如,在OS/2环境中使用操作系统函数DosCreatePipe()创建匿名管道:

int mian( void ){ PFHILE readHandle;PFHILE writeHandle;DosCreatePipe( readHandle, writeHandle, size );………}

在WINDOWS下例如我写的ASM集成环境通过管道与DOS命令行通信的MFC下代码块:

void CIDEManager::Commond( CString cmd, char* buf, unsigned int bufsize ){ SECURITY_ATTRIBUTES sa; HANDLE hRead, hWrite; sa.nLength = sizeof( SECURITY_ATTRIBUTES ); sa.lpSecurityDescriptor = NULL;sa.bInheritHandle = TRUE; if ( !CreatePipe( &hRead, &hWrite, &sa, 0 ) ) // 创建管道 { return; } STARTUPINFO si; PROCESS_INFORMATION pi; si.cb = sizeof( STARTUPINFO ); GetStartupInfo( &si ); si.hStdError = hWrite; si.hStdOutput = hWrite; si.wShowWindow = SW_HIDE; si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; if ( !CreateProcess( NULL, ( LPTSTR )( LPCTSTR )cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi ) ) { return; } CloseHandle( hWrite ); DWORD bytesRead; while ( TRUE ) { memset( buf, 0, bufsize ); if ( ReadFile( hRead, buf, bufsize, &bytesRead, NULL ) != NULL ) { break; } Sleep( 200 ); } CloseHandle( hRead ); return;}

命名管道:将管道用作两个无关联进程间的通信渠道,程序员必须使用命名管道,它可以看作一种具有某名字的特殊类型文件。进程可以根据它的名字访问这个管道。通过匿名管道,父和子进程可以单独使用文件描述符来访问他们所共享的管道,因为子进程继承了父进程的文件描述符,同时文件描述符用read()或write()函数的参数。因为无关进程不能访问彼此的文件描述符,所以不能使用匿名管道。由于命名管道提供该管道的一个等价文件名,任何知道此管道名字的进程都可以访问它。下面是命名管道相对于匿名管道的优点:

命名管道可以被无关联进程使用。

命名管道可以持久。创建它的程序退出后,它们仍然可以存在。

命名管道可以在网络或分布环境中使用。

命名管道容易用于多对一关系中。

与访问匿名管道一样,命名管道也是通过read()或write()函数来访问。两者之间的主要区别在于命名管道的创建方式以及谁可以反问它们。命名管道可以建立一个进程间通信的C/S模型。访问命名管道的进程可能都位于同一台机器上,或位于通过网络通信的不同机器上。由于管道的名字可以通过管道所在服务器的逻辑名,所以能够跨网络访问管道。例如,ServerName//Pipe//MyPipe(不区分大小写)可以作为一个管道名字。假如Server1是网络服务器的名字。当打开或访问这个管道的调用解析文件名时,首先应该定位Server1,然后访问MyPipe。例子如下:

服务器端:

int main( void ){ HANDLE pipehandle; char buf[ 256 ]; DWORD bytesRead; if( ( pipehandle = CreateNamedPipe( ".//Pipe//cao", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 5000, NULL ) ) == INVALID_HANDLE_VALUE ) { printf( "CreateNamedPipe failed with error %d/n", GetLastError() ); system( "pause" ); return 0; } printf( "server is running/n" ); if( ConnectNamedPipe( pipehandle, NULL ) == 0 ) { printf( "connectNamedPipe failed with error %d/n", GetLastError() ); CloseHandle( pipehandle ); system( "pause" ); return 0; } if( ReadFile( pipehandle, buf, sizeof( buf ), &bytesRead, NULL ) == 0 ) { printf( "ReadFile failed with error %d/n", GetLastError() ); CloseHandle( pipehandle ); system( "pause" ); return 0; } printf( "%s/n", buf ); if ( DisconnectNamedPipe( pipehandle ) == 0 ) { printf( "DisconnectNamedPipe failed with error %d/n", GetLastError() ); CloseHandle( pipehandle ); system( "pause" ); return 0; } system( "pause" ); return 0;}

客户端:

int main( void ){ HANDLE pipehandle; DWORD writesbytes; char buff[ 256 ]; if( WaitNamedPipe( ".//Pipe//cao", NMPWAIT_WAIT_FOREVER ) == 0 ) { printf( "WaitNamedPipe failed with error %d/n", GetLastError() ); system( "pause" ); return 0; }

if( ( pipehandle = CreateFile( ".//Pipe//cao", GENERIC_READ | GENERIC_WRITE, 0, ( LPSECURITY_ATTRIBUTES )NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, ( HANDLE )NULL ) ) == INVALID_HANDLE_VALUE ) { printf( "CreateFile failed with error %d/n", GetLastError() ); system( "pause" ); return 0; } ZeroMemory( &buff, sizeof( buff ) ); gets( buff ); if( WriteFile( pipehandle, buff, sizeof( buff ), &writesbytes, NULL ) == 0 ) { printf( "WriteFile failed with error %d/n", GetLastError() ); CloseHandle( pipehandle ); system( "pause" ); return 0; } printf( "write %d bytes", writesbytes ); CloseHandle( pipehandle ); system( "pause" ); return 0;}

命名管道不仅可用于无关联进程间、位于不同机器上的两进程间的通信,而且可用于多对一通信,可以建立服务器进程,允许同时通过多个客户访问命名管道。命名管道常常用于多线程服务器。

前面提到了几种方式,还要下面几种方式实现进程间的通信:

四、 共享内存

进程间共享内存的关系与函数间全局变量的关系相似。程序中的所有函数都可以使用全局变量的值。同样,共享内存块可以被正在执行的所有进程访问。内存块可能共享一个逻辑地址,进程也可以共享某些物理地址。

共享内存块的创建必须由一个系统API调用来完成。在WIN32环境中,使用CreateFileMapping()、MapViewOfFile()以及MapViewOfFileEx() API能很好地完成。

共享内存分配位于WIN32系统中2~3GB地址范围内。一旦调用MapViewOfFile()和MapViewOfFileEx(),共享文件映射对象的所有进程都可以立即访问此内存块,而且在需要时,可以读写此内存块。

五、动态数据交换

动态数据交换( dynamic data exchange ) 是当今可用的进程间通信最强大和完善的形式之一。动态数据交换使用消息传递、共享内存、事务协议、客户/服务器范畴、同步规则以及会话协议来让数据和控制信息在进程间流动。动态数据交换对话( dynamic data exchange session, DDE )的基本模型是客户、服务器。服务器对来自客户的数据或动作作出反应。客户和服务器可以以多种关系来通信。

一个服务器可以与任意数量的客户通信。一个客户也可以与任意数量的服务器通信。单个DDE代理既可以是客户,也可以是服务器。也就是说,进程可以从一个正为另一个进程执行服务的DDE代理请求服务。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Linux的进程与线程
下一篇:在Linux下开发微信小程序(csdn微信小程序)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~