匿名管道之“第三者插足”
一、管道
管道是unix系统的一个重要特征,也正是管道让shell可以把各种不同功能的程序和谐的连接在一起。匿名管道并不是通过open系统调用来创建的,也不是用mknod创建,使用了自己的一个专用通道pipe系统调用。由于匿名管道没有名字,所以只能在父进程和子进程之间共享,当然进程也可以在多线程中用管道和自己通讯,但是这个感觉意义不大,就好像一个人买两个手机,然后自己给自己打电话,一个打,一个接。那这个人要么是有钱烫的很,要么是手机部的同事在做测试。
匿名管道和命名管道(fifo)不同,它对外界是不可见的,只能通过pipe系统调用返回的fd访问,而fd这个东西又是一个进程私有的概念,同样的数值,在不同的进程中表示不同的文件。这一点和普通文件及fifo不同,这些文件只要通过绝对路径,大家都可以打开,然后插上一腿,刻上“XXX到此一游”。那么匿名管道真的就是滴水不漏了吗?
二、入侵
主要是看到了proc文件系统下一个进程的文件描述符,这个文件描述符不仅可以看,而且还真的就可以打开,例如,我在一个控制台中执行
[root@Harry ~]# sleep 1000 | more
然后打开另一个bash,通过 ps看到这个管道两端的进程
[root@Harry ~]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 2044 632 ? Ss Jan28 0:02 /sbin/init
……
root 8762 0.0 0.0 3940 480 pts/0 S+ 23:05 0:00 sleep 1000
root 8763 0.0 0.0 4052 596 pts/0 S+ 23:05 0:00 more 这是管道的输入方一端
root 8764 0.0 0.0 4684 984 pts/2 R+ 23:05 0:00 ps aux
[root@Harry ~]# ls -l /proc/8763/fd
total 0
lr-x------. 1 root root 64 2012-01-30 23:06 0 -> pipe:[93840]
lrwx------. 1 root root 64 2012-01-30 23:06 1 -> /dev/pts/0
lrwx------. 1 root root 64 2012-01-30 23:05 2 -> /dev/pts/0
[root@Harry ~]# echo invader > /proc/8763/fd/0 通过echo工具来向more的标准输入中打印invader
然后在管道所在窗口中可以看到下面的输出
[root@Harry ~]# sleep 1000 | more
invader
也即是通过proc中fd机制成功的入侵了管道。
三、总结
如果看官您问码了这么多字啥意义?想说明个啥问题?蛋疼不疼?实不相瞒,我还真没有想到有啥意义,但是感觉挺有意思的,Just for fun?或者是人与人之间交流在操作系统中的一个缩影?原以为牢不可破的通道可以轻松的被第三者(第N者)搅局,而另一方(甚至是双方)都毫不知情,还以为忠贞不渝。
四、todo
想到有两个问题:
1、多读者/多写者时如何唤醒
在多个进程向一个管道中写入或者多个进程从一个管道中读出的时候,当另一侧准备好的时候,是所有进程都被唤醒还是只唤醒一个?这个我大致看了一下,觉得是只唤醒一个。现在假设说有多个进程在读一个管道,当管道有写入时,此时所有的读端是不是严格轮流来获得这个输入?我同样感觉是的。
2、more如何接收翻屏命令
当用more作为管道的读入端的时候,可以看到,它的标准输入是sleep的输出。但是more会在一屏打满之后暂停,等待键盘输入空格来继续。这里大家不知道有没有注意到,这more是如何感觉到键盘按键的?再次强调,它的标准输入是sleep的输出而不是控制台。在下同样是看了一下more的代码,发现这货是从标准错误中读入命令的,虽然大家通常都是向标准错误中打印的,但是你要硬是从里面读,也是可以的,而且有时候还可以收到奇效。贴一下more里的读取代码吧,以免这篇文章显的单薄
int readch () {
unsigned char c;
errno = 0;
if (read (fileno(stderr), &c, 1) <= 0) {
if (errno != EINTR)
end_it(0);
else
c = otty.c_cc[VKILL];
}
return (c);
}