进程间通讯 IPC interprocess communication

1,管道,FIFO
2, 信号
3,消息队列
4,共享类存linux

5.文件映射程序员

6.socket服务器


1)管道(Pipe):管道可用于具备亲缘关系进程间的通讯,容许一个进程和另外一个与它有共同祖先的进程之间进行通讯。
  (
2)命名管道(named pipe):命名管道克服了管道没有名字的限制,所以,除具备管道所具备的功能外,它还容许无亲缘关系进程间的通讯。命名管道在文件系统中有对应的文件名。命名管道经过命令mkfifo或系统调用mkfifo来建立。
  (
3)信号(Signal):信号是比较复杂的通讯方式,用于通知接受进程有某种事件发生,除了用于进程间通讯外,进程还能够发送信号给进程自己;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又可以统一对外接口,用sigaction函数从新实现了signal函数)。
  (
4)消息(Message)队列:消息队列是消息的连接表,包括Posix消息队列system V消息队列。有足够权限的进程能够向队列中添加消息,被赋予读权限的进程则能够读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
  (
5)共享内存:使得多个进程能够访问同一块内存空间,是最快的可用IPC形式。是针对其余通讯机制运行效率较低而设计的。每每与其它通讯机制,如信号量结合使用,来达到进程间的同步及互斥。
  (
6)内存映射(mapped memory):内存映射容许任何多个进程间通讯,每个使用该机制的进程经过把一个共享的文件映射到本身的进程地址空间来实现它。
  (
7)信号量(semaphore):主要做为进程间以及同一进程不一样线程之间的同步手段。
  (
8)套接口(Socket):更为通常的进程间通讯机制,可用于不一样机器之间的进程间通讯。起初是由Unix系统的BSD分支开发出来的,但如今通常能够移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

通讯就是说进程之间传递数据。常见的方法有   pipe(管道),FIFO(命名管道),socket(套接字),SysVIPC   的   shm(共享内存)、msg   queue(消息队列),mmap(文件映射)。之前还有   STREAM,不过如今比较少见了(好像)。  
   
  同步的意思是说,让不一样进程可以在同时到达一个已知的特定状态以前等待另外一方的执行。Linux   下常见的同步方法有SysVIPC   的   sem(信号量)、file   locking   /   record   locking(经过   fcntl   设定的文件锁、记录锁)、futex(基于共享内存的快速用户态互斥锁)。针对线程(pthread)的还有   pthread_mutex   和   pthread_cond(条件变量)。  
  除了这些特定的同步对象以外,还有一些同步方法是与通讯方法不可分离的,包括:对   pipe/FIFO/socket   和   msg   queue   的阻塞等待、对子进程退出事件的等待(wait族)、对线程退出时间的等待(pthread_join)  
   
  另外还有一个不能不提的,就是信号。Unix   信号是异步处理的、能够终端接收进程执行过程的特殊   IPC   方式——其实既能够算同步也能够选通信了。网络

#####################################################################################################################数据结构

  linux下进程间通讯的几种主要手段简介:  
   
        1.   管道(Pipe)及有名管道(named   pipe):管道可用于具备亲缘关系进程间的通讯,有名管道克服了管道没有名字的限制,所以,除具备管道所具备的功能外,它还容许无亲缘关系进程间的通 信;  
        2.   信号(Signal):信号是比较复杂的通讯方式,用于通知接受进程有某种事件发生,除了用于进程间通讯外,进程还能够发送信号给进程自己;linux除 了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数   sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又可以统一对外接口,用sigaction函数从新实现了signal   函数);  
        3.   报文(Message)队列(消息队列):消息队列是消息的连接表,包括Posix消息队列system   V消息队列。有足够权限的进程能够向队列中添加消息,被赋予读权限的进程则能够读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺点。  
        4.   共享内存:使得多个进程能够访问同一块内存空间,是最快的可用IPC形式。是针对其余通讯机制运行效率较低而设计的。每每与其它通讯机制,如信号量结合使 用,来达到进程间的同步及互斥。  
        5.   信号量(semaphore):主要做为进程间以及同一进程不一样线程之间的同步手段。  
        6.   套接口(Socket):更为通常的进程间通讯机制,可用于不一样机器之间的进程间通讯。起初是由Unix系统的BSD分支开发出来的,但如今通常能够移植 到其它类Unix系统上:Linux和System   V的变种都支持套接字。app

#####################################################################################################异步

用于进程间通信(IPC)的四种不一样技术:   
1. 消息传递(管道,FIFO,posix和system v消息队列)
2. 同步(互斥锁,条件变量,读写锁,文件和记录锁,Posix和System V信号灯)
3. 共享内存区(匿名共享内存区,有名Posix共享内存区,有名System V共享内存区)
4. 过程调用(Solaris门,Sun RPC)
消息队列和过程调用每每单独使用,也就是说它们一般提供了本身的同步机制.相反,共享内存区一般须要由应用程序提供的某种同步形式才能正常工做.解决某个特定问题应使用哪一种IPC不存在简单的断定,应该逐渐熟悉各类IPC形式提供的机制,而后根据特定应用的要求比较它们的特性.

必须考虑的四个前提:
1. 联网的仍是非联网的.IPC适用于单台主机上的进程或线程间的.若是应用程序有可能分布到多台主机上,那就要考虑使用套接字代替IPC,从而简化之后向联网的应用程序转移的工做.
2. 可移植性.
3. 性能,在具体的开发环境下运行测试程序,比较几种IPC的性能差别.
4. 实时调度.若是须要这一特性,并且所用的系统也支持posix实时调度选项,那就考虑使用Posix的消息传递和同步函数.

各类IPC之间的一些主要差别:
1. 管道和FIFO是字节流,没有消息边界.Posix消息和System V消息则有从发送者向接受者维护的记录边界(eg:TCP是没有记录边界的字节流,UDP则提供具备记录边界的消息).
2. 当有一个消息放置到一个空队列中时,Posix消息队列可向一个进程发送一个信号,或者启动一个新的线程.System V则不提供相似的通知形式.
3. 管道和FIFO的数据字节是先进先出的.Posix消息和System V消息具备由发送者赋予的优先级.从一个Posix消息队列读出时,首先返回的老是优先级最高的消息.从一个System V消息队列读出时,读出者能够要求想要的任意优先级的消息.
4. 在众多的消息传递技术—管道,FIFO,Posix消息队列和System V消息队列—中,可从一个信号处理程序中调用的函数只有read和write(适用于管道和FIFO).

比较不一样形式的消息传递时,咱们感兴趣的有两种测量尺度:
1. 带宽(bandwidth):数据经过IPC通道转移的速度.为测量该值,咱们从一个进程向另外一个进程发送大量数据(几百万字节).咱们还给不一样大小的I/O操做(例如管道和FIFO的write和read操做)测量该值,期待发现带宽随每一个I/O操做的数据量的增加而增加的规律.
2. 延迟(latency):一个小的IPC消息从一个进程到令一个进程再返回来所花的时间.咱们测量的是只有一个1个字节的消息从一个进程到令一个进程再回来的时间(往返时间)

在现实世界中,带宽告诉咱们大块数据经过一个IPC通道发送出去需花多长时间,然而IPC也用于传递小的控制信息,系统处理这些小消息所需的时间就由延迟提供.这两个数都很重要.

#########################################################################################################################socket

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

通常而言,进程有单独的地址空 间。咱们能够了解下可执行程序被装载到内存后创建的一系列映射等理解这一点。如此以来意味着若是咱们有两个进程(进程A和进程B),那么,在进程A中声明的数据对于进程B是不可用的。并且,进程B看不到进程A中发生的事件,反之亦然。若是进程AB一块儿工做来完成某个任务,必须有一个在两个进程 间通讯信息和时间的方法。咱们这里能够去看看基本的进程组件。注意进程有一个文本、数据以及堆栈片段。进程可能也有从自由存储空间中分配的其它内存。进程 所占有的数据通常位于数据片段、堆栈片段或进程的动态分配内存中。数据对于其它进程来讲是受保护的。为了让一个进程访问另一个进程的数据,必须最终使用 操做系统调用。与之相似,为了让一个进程知道另外一个进程中文本片段中发生的事件,必须在进程间创建一种通讯方式。这也须要来自操做系统API的帮助。当进程将数据发送到另外一进程时,称作IPCinterprocess communication,进程间通讯)。下面先列举几种不一样类型的进程间通讯 方式:函数

 

进程间通讯                                 描 述

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

 

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

 

管道                        用于相关和无关进程间的通讯,并且造成两个进程间的一个通讯通道,一般使用文件读写程序访问。

 

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

 

DDE                           (动态数据交换Dynamic data exchange

                                使用客户机/服务器模型(C/S),服务器对客户的数据 或动做请求做出反应。

 

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

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

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

程从父进程继承资源的过程创造了进程间通讯的一个机会。父进程能够在它的数据片段或环

境中设置必定的变量,子进程因而接受这些值。一样,父进程也能够打开一个文件,推动到

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

 

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

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

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

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

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

 

2、命令行参数:

经过命令行参数(command-line argument)能够完成另外一种单向、一次性的进程间通讯

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

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

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

exe程序的API。你们能够去参考一下ShellExecuteA函数相关。

 

除了文件描述符外,继承资源是IPC的单向、一次 性形式。

传递命令参数也是单向、一次性的IPC方 法。

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

还有另外一种结构,称作管道(Pipe), 它 能够用于在关联进程间以及无关联进程间进行通讯。

 

3、管道通讯:

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

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

管道结构经过使用文本和写方式来访问。若是进程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;

相关文章
相关标签/搜索