首页 > 代码库 > 匿名管道
匿名管道
1关于管道
有两种类型的管道:匿名管道和命名管道。匿名管道比命名管道需要更少的开销,但是提供有限的服务。
术语管道,就像用在这里的,暗示管道被用作一个信息管子。从概念上讲,一个管道有两端。单向管道允许进程在一端写入,并且允许进程在令一端读入。双向管道允许进程在同一段读写。
- 匿名管道
- 命名管道
1.1 匿名管道
匿名管道是一种没有名字的,通常在父子进程之间传递数据的管道。匿名管道是本地的;它们不能用在网络之间的通信。
- 匿名管道操作
- 管道句柄继承
- 匿名管道安全和访问权限
1.1.1匿名管道操作
CreatePipe函数创建一个匿名管道并且返回两个句柄:一个读向管道的句柄和一个写向一个管道的句柄。读句柄对管道有只读的权利,写句柄有只写的权利。为了用管道进行通信,管道服务器必须把管道句柄传递给另一个进程。通常,这是通过继承来实现的;也就是说进程允许句柄被子进程继承。进程也可以通过DuplicateHandle函数来复制管道句柄并且用某种进程间通信方式例如DDE,或者共享内存把它发送到一个无关的进程。
管道服务器可以把读句柄或者写句柄任何一个发送到管道客户端,依赖于客户端是否用匿名管道来发送或者接受信息。若要从管道读取信息,在调用函数中ReadFile中使用管道的读句柄。当另一个进程已经写进管道的时候ReadFile调用可以返回。如果管道的写入句柄被关闭或者在读操作完成之前发生错误,ReadFile也可以返回。
为了写入管道,在调用WriteFile函数的时候使用管道的写句柄。WriteFile直到它写完一定数目的字节数或者错误发生才返回。如果管道满了并且还有更多的字节要写入,WriteFile不会返回直到另一个进程从管道读取信息来腾出更多的可用缓冲空间。管道服务器在调用CreateFile的时候指定管道缓冲区的大小。
匿名管道不支持异步(重叠)的读写操作。这意味着在匿名管道中不能使用ReadFileEx和WriteFileEx函数。此外,ReadFile和WriteFile中的lpOverlapped参数会被忽略当它们是在匿名管道中使用的时候。
匿名管道会一直存在直到管道的句柄包括读和写句柄都被关闭。进程可以使用CloseHandle函数来关闭它的管道句柄。当进程终结的时候所有的管道句柄也会被关闭。
匿名管道是命名管道使用一个独一无二的名字来实现的。因此可以在传递一个匿名管道句柄给一个本该需要传递命名管道句柄参数的函数。
1.1.2管道句柄继承
管道服务器控制它的句柄是否可以按照以下的方式来继承
- CreatePipe函数接受一个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都会返回,此时函数不会阻塞。如果管道中有东西可以读的话就进行读取,然后返回TRUE;没有东西读的话就会返回FALSE。但是如果有写入端句柄没关闭的情况下,在进行ReadFile的时候情况就不一样了。如果管道中有东西的话,ReadFile就会返回TRUE;如果没有东西的话ReadFile就会一直处于阻塞状态,直到管道的写入句柄进行写入操作。原因也很简单,因为写入句柄没关闭,所以ReadFile函数就会等待数据写入,如果写入句柄已经关闭,那么就没有等待的必要了。总之,MSDN上的话跟实际的有出入)
重定位标准输入的过程跟重定位标准输出句柄的过程很类似,除了管道的读句柄被用作子进程的标准输入句柄。在这种情形下,父进程必须保证子进程没有继承管道的写入句柄。如果这没有做的话,子进程中的ReadFile函数的操作就不会返回0因为子进程在写入端有打开的句柄。
1.1.3匿名管道安全和访问权限
Windows的安全属性能让你控制匿名管道的访问控制。
你可以在调用CreatePipe的时候指定security descriptor。Security descriptor管道读写端的访问权限。如果你指定为NULL,那么管道获得一个默认的安全描述符。在一个管道中的默认安全描述符中的访问控制列表来自于创造者的主要或模拟令牌(?)。
为了得到管道的安全描述符,可以调用GetSecurityInfo函数。为了改变管道的安全描述,调用SetSecurityInfo函数。
CreatePipe函数返回匿名管道的两个句柄:一个带GENERIC_READ和SYNCHRONIZE的读句柄;一个带有GENERIC_WRITE和SYNCHONIZE的写句柄。GENERIC_READ和GENERIC_WRITE访问用和命名管道中相同的访问权限。
匿名管道中的GENERIC_READ把访问管道数据,读取管道属性,读取扩展属性和读取管道的DACL结合在一起。
匿名管道中的GENERIC_WRITE访问把向管道写入数据,添加数据,写入管道属性,写入扩展属性,管道的DACL的写入结合在一起(这里对原意有改动,原句是and read the pipe‘s DACL.)。
匿名管道