popen是高级的函数,底层的使用pipe函数。
pipe这个函数在两个程序之间传递数据不需要启动一个shell来解释请求的命令。它同时提供了对读写数据的更多控制。
头文件: unstd.h
int pipe(int file_descriptor[2]);
pipe 函数的参数是一个由两个整数类型的文件描述符组成的数组的指针。该函数在数组中填上两个新的文件描述符返回0,如果失败就会返回-1并设置errno来表明失败的原因。
linux手册上定义的一些错误:
(1)EMFILE: 进程使用的文件描述符过多
(2)ENFILE: 系统的文件表已经满
(3)EFAULT: 文件描述符无效
两个返回的问阿金描述符以一种特殊的方式连接起来。写到file_descriptor[1] 的所有数据都可以从file_descriptor[0]读回来。数据基于先进先出的原则(FIFO)进行处理,这意味着如果你把字节1,2,3写到file_descriptor[1],从file_descripter[0]读取到的数据也会是1,2,3.这个和栈有点不一样,栈是后进先出[LIFO];这个应该算是队列。
特别要注意:这里使用的是文件描述符而不是文件流,所以我们必须用底层的read和write调用阿里访问数据,而不是用文件流库函数fread 和fwrite。
PS:底层的有底层的方法,底层不可能直接通过流来处理,而是通过文件描述符。
这个程序用数组file_pipes[]中的两个文件描述符创建了一个管道。然后它用文件描述符file_pipes[1]向管道写数据,在从file_pipes[0]读取数据。注意,管道有一些内置的缓存区,它在write和read调用之间保存数据。
如果你尝试用file_descriptor[0]写数据或用file_descriptor[1]读数据,其后果未在文档中明确定义,所以其行为可能会非常奇怪,并且随着系统的不同,其行为可能发生变化。在我的系统上,这样的调用将失败并且返回-1,这至少能够说明这种错误比较容易发现的。
咋一看:管道没有什么特别,可以用一个简单的文件完成;
管道真正的优势在于:当你两个进程之间传递数据的时候。
(12章)当程序用fork调用创建新进程时候,原先打开的文件描述符忍将保持打开状态。如果在原先的进程中创建一个管道。然后在调用fork创建新继承,我们即可通过管道在两个进程之间传递数据。
实验解析:
这个程序首先用pipe调用创建一个管道,接着用fork调用创建一个新进程。如果fork调用成功,父进程就写数据到管道中,而子进程从管道中读取数据。父进程都在只调用了一次write 或read之后就会退出。如果父进程在子进程之前退出,就会在两部分输出之间看到shell提示符(shell上运行)。
表面上:这个程序和上面的程序例子很相似,但是实际上这个例子中我们往前跨除了一大步,我们可以在不同的进程之间进行了读写操作了。
PS: pipe 的作用:
(1)一个是和文件写入和读取有点类似
(2)重点:可以在有亲缘关系的进程之间传递数据。