[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[系统相关] 分析CMD管道命令

  1. 引言:
  2. 最近在看一本关于计算机操作系统的书,有一章节讲的内容是程序的进程。
  3. 程序的进程之间有三种通信机制:1.共享存储器系统 2.消息传递系统 3.管道通信
  4. 其中消息传递系统是当前应用最为广泛的一种通信机制。熟悉WINDOWS编程的朋友应该不会陌生。
  5. 书中短短一小段话就把管道通信介绍完毕,可见这部分内容并不是非常重要。
  6. 但是我却对管道通信起了兴趣,想起了CMD下的管道命令,我决定深入了解一番。
复制代码
我们先来了解什么是管道,书中是这样定义的:
所谓“管道”,是指用于连接一个读进程和一个写进程以实现他们之间通信的一个共享文件,又名pipe文件。

在这里,许多人会这样想,“一个文件?那不是硬盘上找的到,在什么位置呢?”
我先跟大家提一下,管道“文件”是由API函数CreateFile创建的,
管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,
它不属于某种文件系统(如FAT32,NTFS,ext3),而是自立门户,单独构成一个文件系统,并且只存在于内存中。
这很类似系统的“设备文件”,所以大家把“管道文件”当作一个缓冲区就可以了。

管道的命名必须遵循特定的命名方法,就是 "\\.\pipe\管道名",当作为客户端的进程要使用时,使用"\\计算机名\\pipe\管道名" 来打开使用。
按照管道的类别分有两种管道,匿名的和命名的;按照管道的传输方向分也可以分成两种,单向的双向的。
根据管道的特点,命名管道通常用在网络环境下不同计算机上运行的进程之间的通信(当然也可以用在同一台机的不同进程中)它可以是单向或双向的;
而匿名管道只能用在同一台计算机中,它只能是单向的。匿名管道其实是通过用给了一个指定名字的有名管道来实现的。

在编程方面的知识我就不扯太多了,有兴趣请自己研究,我在文章结尾会附上参考文档。
CMD下的管道命令 "|" 就是匿名管道。

在CMD下运行:
  1. set input=empty
  2. echo test | set /p input=
  3. echo %input%
复制代码
  1. C:\Users\Broly>set input=empty
  2. C:\Users\Broly>echo test | set /p input=
  3. C:\Users\Broly>echo %input%
  4. empty
复制代码
结果显示的是empty,也就是说第二行的语句好像是没有赋值成功的。

再来看看这个代码:
  1. set input=empty
  2. echo test | (set /p input= & set input)
  3. echo %input%
复制代码
  1. C:\Users\Broly>set input=empty
  2. C:\Users\Broly>echo test | (set /p input= & set input)
  3. input=test
  4. C:\Users\Broly>echo %input%
  5. empty
复制代码
结果显示,管道后面的变量input事实上是被赋值成功的。
那为什么第三条语句会显示input变量的值为empty呢?

因为管道命令 "|" 后面的语句,CMD会新创建一个子进程,
第二句的input实际为一个新的CMD进程里面的变量,
而第三句的input为父进程的变量,所以两个变量的值就不同。

值得注意的是,管道命令 "|" 后面的命令不管是内部命令还是外部命令,CMD都会为其创建新的子进程。

conset工具的实现原理应该就是获取另一个进程的结果再赋值给当前进程的变量。

关于重定向命令:
  1. stdin是标准输入,stdout是标准输出,stderr是标准错误输出。
  2. 大多数的命令行程序从stdin输入,输出到stdout或stderr,有时我们需要重定向stdout,stderr,stdin。
  3. 在Windows编程中,重定向需要用到管道(Pipe)的概念。管道是一种用于在进程间共享数据的机制。一个管道类似于一个管子的两端,一端是写入的,一端是读出的。由一个进程从写入端写入、另一个进程从读出端读出,从而实现通信,就向一个“管道”一样。
  4. 重定向的原理是:
  5. 首先声明两个概念:主程序(重定向的操纵者)、子进程(被重定向的子进程)
  6. 如果要重定位stdout的话,先生成一个管道, 管道的写入端交给子进程去写,主程序从管道的读出端读数据,然后可以把数据写成文件、显示等等。重定向stderr和stdout是相同的。
  7. 同理,要重定向stdin的话,生成一个管道, 管道的写入端由主程序写,子进程从管道的读出端读数据。
复制代码
大概重定向命令和管道命令的过程就是这样,不是很难理解。
以上部分文字是我直接网上复制过来的,有错误的地方望给予指出。

参考资料:http://msdn.microsoft.com/en-us/library/windows/desktop/aa365781(v=vs.85).aspx
---学无止境---

回复 2# Bearxy
    这位朋友,请你确定自己把整篇文章都理解了再提问吧,是细心看完而不是随便一目十行的那种。如果还有疑问我一一解答。
---学无止境---

TOP

为了让大家更容易理解,我还是详细再说说吧:
当cmd运行一个内部命令时,它是不会创建子进程的,仅是当前cmd进程。
而运行一个外部命令时,cmd会创建一个子进程,也就是外部命令的进程。

而管道命令有一点可以明确的是,它做进程之间的通信用,所以父进程的数据可以通过管道传递给子进程。
如果管道后面的命令是内部命令,内部命令需要的是cmd的支持,所以创建的子进程是cmd.exe。而外部命令则不会再创建一个cmd.exe,依旧是本身进程做子进程。

像一楼的例子中set是一个内部命令,将会产生一个新cmd.exe子进程,而input变量的值就是由管道传过来的值,input变量是存在于子进程cmd.exe中。

这样详细说明,还有谁不能够理解?
---学无止境---

TOP

回复 5# Demon
    文中只是说重定向要用到管道的概念,用这个概念分析挺合适的啊。

其实我也有用Od分析过|,解析的过程也就是文中说的那样,你也可以去看看。至于说技术内幕这个系列,还是交给你来合适,我也只是像引言提到的,突然来兴趣了才研究下的。
---学无止境---

TOP

本帖最后由 broly 于 2012-9-15 01:23 编辑

回复 7# plp626


    刚才看了下程序,发现主要是用到了两个API函数:GetEnvironmentVariable,SetEnvironmentVariable

  去MSDN找了些有用的资料,可以参考:http://msdn.microsoft.com/zh-cn/ ... loads/ms682009.aspx

今晚跟朋友出去了,回来的好晚,我要睡觉了,明天有空再试试资料中的例子代码。
---学无止境---

TOP

返回列表