您的位置:首页 > 编程语言 > PHP开发

管道(Pipes)

2008-09-15 10:26 225 查看
说明:此文档由MSDN翻译而来

管道概述<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

    管道就是进程之间用来通信的一块共享内存.建立管道的进程被称为管道服务器,其它连接到管道上的进程被称为管道客户.一个进程向管道中写入信息,然后其它的进程则可以从管道中读取该信息.下面来讨论如何创建,管理和使用管道.
 

有两种类型的管道:匿名管道命名管道.匿名管道的操作比命名管道简单,但是能够提供的服务也是有限的.
管道,这个术语本身就暗示了它是一个信息的渠道.从概念上讲,一个管道有两端.单向管道限制一端的进程只能向管道中写,而另一端的进程只能从管道中读.双向管道则允许两端的进程向管道中写入或读出.

匿名管道

匿名管道是一个没有命名的单向管道,通常用来在父进程和子进程之间传输数据.匿名管道总是本地的,即它不能用来跨网络传输信息.

匿名管道操作

函数CreatePipe可以创建一个匿名管道并返回两个句柄:一个读管道句柄和一个写管道句柄.读管道句柄对管道只有读权限,写管道句柄对管道则只有写权限.为了使用管道进行交流,管道服务器必须把其中的一个管道句柄传递给另外一个进程.通常这是通过继承来完成的,即管道服务器允许管道句柄被子进程继承.管道服务器还能够使用DuplicateHandle函数来复制管道句柄,然后通过某种形式的进程间交互(如DDE或共享内存)将句柄传递给一个不相关的进程.
管道服务器可以把只读句柄或只写句柄中的任何一个传递出去,这完全取决于管道客户的需求.可以在ReadFile函数中使用管道的读句柄来从管道中读取信息.ReadFile在下列情况下会返回:别的进程向管道中写入了信息后;所有的写管道句柄都关闭了;读管道出错. 
可以在riteFile函数中使用管道的写句柄来向管道中写信息.WriteFile函数直到写完指定数量字节的信息或者写入出错时才会返回.如果管道已经满了,WriteFile会等待别的进程从管道中读出信息,为管道腾出空间写入剩下的信息才返回.管道服务器在使用CreatePipe函数创建管道时会指定管道的大小.
匿名管道不支持异步读写操作.这意味着你不能对匿名管道使用ReadFileEx和WriteFileEx操作.而且ReadFile和WriteFile的lpOverlapped参数也是被忽略的,当其对匿名管道进行操作时.
匿名管道会一直存在,直到它的所有读和写句柄都关闭.一个进程可以使用CloseHandle函数关闭它的管道句柄.进程终止后,它的所有管道句柄会自动关闭.
从实现上讲,匿名管道其实是一个有着特殊名字的命名管道.因此,许多时候你可以将匿名管道的句柄传递给一个需要命名管道句柄的函数.

管道句柄继承

管道服务器可以通过下列方式来控制它的管道句柄能够被继承:
·CreatePipe函数接受SECURITY_ATTRIBUTES结构.如果管道服务器将SECURITY_ATTRIBUTES结构的bInheritHandle成员置为TRUE时,则CreatePipe返回的句柄是可以被继承的;
·管道服务器可以使用DuplicateHandle函数来改变管道的继承性.管道服务器能够创建一个可继承句柄的不可继承复本;或者不可继承句柄的可继承复本;
·管道服务器可以使用CreateProcess函数来指定子进程继承它的所有句柄或者不继承它的任何句柄.
当一个子进程继承了管道句柄之后,操作系统就会允许它访问这个管道.不过,父进程必须把这个句柄值传递给子进程.典型情况下,父进程会重定向标准输出句柄到子进程,具体步骤如下:
1.父进程(管道服务器)调用GetStdHandle函数获取当前标准输出的句柄;保存该句柄,以便在子进程被创建后可以恢复这个原始的标准输出句柄
2.调用SetStdHandle函数,将标准输出句柄替换为管道句柄.然后父进程就可以创建子进程了.
3.调用CloseHandle函数关闭写管道句柄.一旦子进程继承了写管道句柄之后,父进程则没有必要保存它的复本了.
4.调用SetStdHandle函数恢复原始的标准输出句柄.
子进程使用GetStdHandle函数获取标准输出句柄,其实这时候标准输出句柄已经被更换为管道句柄了.于是子进程就可以使用WriteFile函数向管道中发送信息了.当子进程不再需要向管道中发送信息时,就应当使用CloseHandle函数来关闭管道句柄.当然在子进程终止后,该句柄也会自动关闭.
父进程使用ReadFile函数从管道中接受输入.数据作为字节流被写入到管道.这意味着从管道中读信息的父进程无法区分开两个独立的写操作所写入的数据,除非父进程和子进程都遵守某种协议以指示一个写操作的结束.当所有写管道句柄都关闭了时,ReadFile函数返回0.因此,父进程在调用ReadFile函数之前应该关闭自己的所有的写管道句柄,这是很重要的.否则,ReadFile函数永远不会返回0,因为父进程还有一个打开的写管道句柄.
重定向标准输入句柄的步骤与重定向标准输出是相似的,不同的是读管道句柄用作子进程的标准输入句柄.这时,父进程必须保证子进程没有继承它的写管道句柄.否则,子进程的ReadFile操作就不会返回0了.
附录一是一个使用匿名管道重定向子进程标准输入输出的例子.

匿名管道安全和访问权限

Windows NT安全系统允许你对匿名管道的访问权限进行控制。
在使用CreatePipe函数创建管道时,你可以为管道指定一个安全描述符(security descriptor)。安全描述符既能控制着管道的读和写。如果你将安全描述符指定为NULL,则管道获得一个默认的安全描述符。
调用GetSecurityInfo函数可以获取管道的安全描述符,而SetSecurityInfo函数则可以设置安全描述符。
 

命名管道

一个命名管道可以有多个实例,所有的实例共享同一个名字,但是每个实例都有自己的缓冲区和句柄,为客户和服务器的交流提供各自独立的通道。实例的运用使得多个管道客户能够同时使用相同名字的管道。
任何一个进程只要遵守安全检查,都能够访问命名管道。因此,命名管道是相关或不相关进程之间进行交流的一种简便的方式。
任何一个进程都可以充当服务器或者客户的角色,这使得p2p交流成为可能。
命名管道可以支持本地进程间的交流,也可以支持网络间的进程之间的交流。一旦服务器在运行,所有的命名管道都可以从远程访问。

管道名称

管道服务器调用CreateNamedPipes函数创建一个或多个命名管道实例的时候必须为该管道指定名字。管道客户在调用CreateFile或CallNamedPipe函数连接到一个命名管道实例时也需要指定管道名字。
在调用CreateFile, WaitNamedPipe或CallNamedPipe等函数时,需要指定管道名,管道名的形式如下:
       //ServerName/pip/PipeName
(其中ServerName是一个远程主机的名字或者一个点号,点号表示本地主机。管道的名字由PipeName指定,可以包含除斜杠之外的任何字符,并且是大小写不敏感的。)
管道服务器不能够为远程主机创建一个管道,因此CreateNamedPipe必须总是使用点号来作为ServerName,如:
//./pipe/PipeName
通常,管道服务器将管道名称传递给管道客户,以便客户能够连接到管道上。而且,客户在编译时候也必须知道管道名称。

命名管道的打开模式(Open Modes)

管道服务器可以有几种不同的打开模式,包括access,overlap和write-through等模式。管道服务器可以通过CreateNamedPipe函数的dwOpenMode参数指定打开模式,而管道客户则能够使用CreateFile函数为他们的管道句柄指定打开模式。
-Access Mode
 

 

 

 

 

 

 

 

 

 

 

 

 

附录一 创建一个标准输入输出被重定向了的子进程

这个例子演示了控制台进程(console process)使用CreateProcess函数如何创建子进程,同时也演示了使用匿名管道重定向子进程标准输入输出的技术.注意命名管道也可以用来重定向进程的标准输入输出.
CreatePipe函数使用SECURITY_ATTRIBUTES结构体来创建读写句柄都可以被继承的两个管道.一个管道的读句柄作为子进程的标准输入,另一个管道的写句柄作为子进程的标准输出.这两个管道句柄都在STARTUPINFO结构体中指定,该结构体使得它们成为标准句柄而被子进程所继承.
父进程使用两个管道的另一端,向子进程的输入中写数据,从子进程的输出中读数据.父进程拥有的两个管道句柄也是可继承的,不过子进程不能真的继承它们.在创建子进程之前,父进程必须使用DuplicateHandle函数来创建程序定义的不能被继承的全局变量hChildStdinWr的复本,然后使用CloseHandle来关闭可继承的句柄.下面便是代码:    

下面是子进程的代码:

The following is the code for the child process. It uses the inherited handles for STDIN and STDOUT to access the pipe created by the parent. The parent process reads from its input file and writes the information to a pipe. The child receives text through the pipe using STDIN and writes to the pipe using STDOUT. The parent reads from the read end of the pipe and displays the information to its STDOUT.

 

 

 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息