举例:基于流的s_pipe函数实现
下面的代码展示了基于流的s_pipe函数的实现。这个实现只是调用了标准的pipe函数,创建全双工的管道。
#include "apue.h"
/*
* Returns a STREAMS-based pipe, with the two file descriptors
* returned in fd[0] and fd[1].
*/
int s_pipe(int fd[2])
{
return(pipe(fd));
}
有名stream pipes简介
一般来说,pipes只能在亲戚进程之间使用,一般是子进程从它们的父进程将pipes继承过来。前面我们看到过非相关进程可以使用FIFOs进行通信,但是这个只提供了单向的通信路径。streams机制为进程提供了一个方法,通过这个方法,进程可以给定文件系统中的一个pipe名字,这解决了单向的FIFOs问题。
我们可以使用fattach函数为Streamspipe提供一个文件系统中的名字。
#include <stropts.h>
int fattach(int filedes, const char *path);
返回:如果成功返回0,如果错误返回1。
参数path必须引用一个已经存在的文件名称,并且调用的进程必须拥有这个文件并且具有对这个文件的写权限,或者以超级用户的身份运行。
一旦一个streams pipe被附加到一个文件系统上面的名称上面,那么相应的文件就变得不可访问了。任何打开这个名称的进程将获得相应的pipe的访问而不是那个文件。任何在pipe附加到该文件之前就打开这个文件的进程仍然可以继续对这个文件进行访问。实际上,这些进程一般不会知道现在这个名称正在引用一个不同的文件。
下图展示了一个附加到路径/tmp/pipe上面的pipe。这个pipe只有一个端被附加到文件系统上面的名字上面,另外一个端用来和打开这个被附加的文件的进程进行通信。尽管可以这样将任何类型的流文件描述符号附加到一个文件系统上面的名字上面,但是fattach函数一般用来为一个stream pipe提供一个名字而使用。
挂载到一个文件系统中的名字上面的pipe
+------------------+
| user process |
+--------------^---+
\
\
v
+------------------+ +------------------+
| stream head | | stream head | /tmp/pipe
+------------^-----+ +---^--------------+
\ pipe /
---------------------
一个进程可以调用fdetach来取消一个streams file和一个文件系统中文件名称之间的关联。
#include <stropts.h>
int fdetach(const char *path);
返回:如果成功返回0,如果错误返回1。
在调用fdetach之后,任何通过打开这个路径访问相应的streams pipe的进程还是会仍然继续对这个stream进行访问,但是之后对这个路径打开进行访问的进程将会访问到这个路径对应的文件系统中的原始的文件。
客服-服务端模式的单一连接问题
尽管我们可以将一个stream pipe的末端附加到文件系统上面的一个路径名字上面,我们在多个客户进程使用有名的streams pipe和服务进程进行通信的时候还是会遇到问题。来自一个客户进程的数据可能会干扰另外一个写入管道的客户进程的数据。尽管我们可以保证客户进程写的数据量少于PIPE_BUF字节这样写操作就是原子的了,我们没有办法向一个特定的客户进程反馈并且保证就是那个客户进程读取到了消息。因为有许多进程读取同一个管道,我们无法控制哪个进程实际读取我们发送的数据。
单一连接问题解决方案
connld的stream module解决了这个问题。在将一个streams pipe附加到一个文件系统中的名字之前,服务进程可以首先将一个connld 模块推送到被附加的管道的末端。这样如下图所示:
为单一连接设置connld
+-----------------+ +-----------------+
| server process | | client process |
+-----------^-----+ +-^---------------+
\ .
\ /tmp/pipe .
+v--------------+ +-------------v+
| stream head | | stream head |
+--------^------+ +------^-------+
| |
| |
\ +-------v------+
\ | CONNLD |
\ +------^-------+
\ pipe |
--------------------------+
在上面的图中,服务进程附加pipe的一个末端到/tmp/pipe上面,这里我们用虚线表示客户进程(client process)正在打开一个附加的streams pipe。一旦打开过程完毕了,那么我们就有了下图所示的情况:
使用connld创建单一连接
+----------------+ +---------------+
| server process | | client process|
+-^-------------^+ +--------------^+
/ \ \
/ /tmp/pipe ----------------\ \
+--------------v-+ +---------------+ +v-------------+ +-v-----------+
| stream head | | stream head | | stream head | | stream head|
+--------^-------+ +-------^-------+ +---------^----+ +------^------+
| | \ pipe /
\ | --------------------
\ +-------v-------+
\ | CONNLD |
\ +-------^-------+
\ pipe |
------------------+
客户进程不会为它打开的管道末端接收一个打开的文件描述符号。相反,操作系统创建一个新的管道然后返回给客户进程一个新的管道末端做为打开/tmp/pipe的结果。系统发送新管道的另外一个端给服务进程,发送的方式是通过将它的文件描述符号在已经存在的附加了文件的的管道上进行传输,这样就会导致在客户进程和服务进程上面只有一个单一的连接了。我们将会在后面看到使用stream pipes传输文件描述符号的机制。
fattach函数构建在mount系统调用之上,这个工具被当做被挂载了的streams。被挂载了的streams和connld模块的这些机制后来被SVR4纳入。
网友评论