📄 00000015.htm
字号:
<HTML><HEAD> <TITLE>BBS水木清华站∶精华区</TITLE></HEAD><BODY><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER>发信人: SuperSB (孤鹰), 信区: Linux <BR>标 题: [转载]unix环境高级编程-15 <BR>发信站: BBS 水木清华站 (Wed Mar 15 14:31:19 2000) <BR> <BR> <BR> <BR> <BR>发信人: taosm (128+64-->cool), 信区: unix <BR>标 题: unix环境高级编程--第15章 高级进程间通信 <BR>发信站: 西十八BBS (Sat Mar 11 14:00:43 2000), 转信 <BR> <BR>第十五章 高级进程间通信 <BR>15.1 引言 <BR>上一章说明了各种UNIX系统提供的IPC经典方法,包括:管道、FIFO、消息队列、信 <BR>号量和共享存储。本章介绍某些高级的IPC以及它们的应用方法,包括:流管道和命 <BR>名流管道。使用这些机制,我们可以在进程间传送打开文件描述符。在分别为每一 <BR>个客户进程提供一个通道的系统中,这些通信机制使客户进程能与精灵服务进程会 <BR>合。4。2BSD和SVR3。2最早提供这些高级形式的IPC,但是至今尚未广泛使用,也缺 <BR>少参政文献。本章中很多思想来自Pressotto和Ritchie[1990]的论文。 <BR>15.2 流管道 <BR>流管道是一个双向(全双工)管道。单个流管道就能向父、子进程提供双向的数据流 <BR>。图15。1显示了观察流管道的两种方式。它与图14.2的唯一区别是双向箭头连线 <BR>,这是因为流管道是全双工的。 <BR> 实例 <BR>我们用一个流管道再次实现了程序14.9的协作进程实例。程序15.1是新的main函数 <BR>。add2协作进程与程序14.8中的相同。程序15.1调用了创建一个流管道的新函数s <BR>_pipe。(在下面将说明该函数的SVR4和4.3+BSD版本。) <BR>图15.1 观察流管道的两种方式 <BR>#include <signal.h> <BR>#include "ourhdr.h" <BR>static void sig_pipe(int); /* our signal handler */ <BR>int <BR>main(void) <BR>{ <BR> int n, fd[2]; <BR> pid_t pid; <BR> char line[MAXLINE]; <BR> if (signal(SIGPIPE, sig_pipe) == SIG_ERR) <BR> err_sys("signal error"); <BR> if (s_pipe(fd) < 0) /* only need a single stream pipe */ <BR> err_sys("pipe error"); <BR> if ( (pid = fork()) < 0) <BR> err_sys("fork error"); <BR> else if (pid > 0) { /* parent */ <BR> close(fd[1]); <BR> while (fgets(line, MAXLINE, stdin) != NULL) { <BR> n = strlen(line); <BR> if (write(fd[0], line, n) != n) <BR> err_sys("write error to pipe"); <BR> if ( (n = read(fd[0], line, MAXLINE)) < 0) <BR> err_sys("read error from pipe"); <BR> if (n == 0) { <BR> err_msg("child closed pipe"); <BR> break; <BR> } <BR> line[n] = 0; /* null terminate */ <BR> if (fputs(line, stdout) == EOF) <BR> err_sys("fputs error"); <BR> } <BR> if (ferror(stdin)) <BR> err_sys("fgets error on stdin"); <BR> exit(0); <BR> } else { /* child */ <BR> close(fd[0]); <BR> if (fd[1] != STDIN_FILENO) { <BR> if (dup2(fd[1], STDIN_FILENO) != STDIN_FILENO) <BR> err_sys("dup2 error to stdin"); <BR> } <BR> if (fd[1] != STDOUT_FILENO) { <BR> if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) <BR> err_sys("dup2 error to stdout"); <BR> } <BR> if (execl("./add2", "add2", NULL) < 0) <BR> err_sys("execl error"); <BR> } <BR>} <BR>static void <BR>sig_pipe(int signo) <BR>{ <BR> printf("SIGPIPE caught\n"); <BR> exit(1); <BR>} <BR>程序15.1 用流管道驱动add2过滤进程的程序 <BR> 父程序只使用fd[0],子程序只使用fd[1] 。因为流管道的每一端都是全双工的 <BR>,所以父进程读、写fd[0],而子程序将fd[1]复制到标准输入和标准输出。图15.2显 <BR>示了由此构成的各描述符。 <BR>图15.2 为协作进程安排的各描述符 <BR>s_pipe函数定义为与标准pipe函数类似。它的调用参数与pipe相同,但返回的描述 <BR>符以读 写方式打开。 <BR>实例一SVR4下的s_pipe函数 <BR>程序15.2是s_pipe函数的SVR4版本。它只是调用创建全双工管道的标准pipe函数 <BR>#include "ourhdr.h" <BR>int <BR>s_pipe(int fd[2]) /* two file descriptors returned in fd[0] & fd[1] */ <BR>{ <BR> return( pipe(fd) ); <BR>} <BR>程序15.2 s_pipe函数的SVR4版本 <BR>在系统V的早期版本中也可以创建流管道,但要进行的处理较多。有关SVR3.2下创建 <BR>流管道的详细情况,请参阅Stevens[1990]。 <BR>图15.3显示SVR4之下管道的基本结构。它主要是两个相互连接的流首。 <BR>图15.3 在SVR4之下的管道 <BR>因为管道是一种流设备,我们可将处理模块压入管道的任一端。在15.5.1节,我们将 <BR>用此技术提供一个可以装配的命名管道。 <BR>实例一4.3+BSD之下的s_pipe函数 <BR>程序15.3是s_pipe函数的BSD版本。此函数在4.2BSD及以后的各版本中起作用。它 <BR>创建一对互连的UNIX域流套接口。 <BR> 自4.2BSD开始,常规的管道已用此方式实现。但是,当调用pipe时,第一个描述 <BR>符的写端和第二个描述符的读端都被关闭。为获得全双工管道,必须直接调用sock <BR>etpair。 <BR>#include <sys/types.h> <BR>#include <sys/socket.h> <BR>#include "ourhdr.h" <BR>int <BR>s_pipe(int fd[2]) /* two file descriptors returned in fd[0] & fd[1] */ <BR>{ <BR> return( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) ); <BR>} <BR>程序15.3 s_pipe函数的BSD版本。 <BR>15.3 传送文件描述符 <BR> 在进程间传送打开文件描述符的能力是非常有用的。用此可以对客户/服务器 <BR>应用进行不同的设计。它允许一个进程(典型地是一个服务器)处理与打开一个文件 <BR>有关的所有操作(涉及的细节可能是:将网络名翻译为网络地址、拨号调制解调器、 <BR>协商文件锁等。)以及向调用进程返回一描述符,该描述符可被用于以后的所有I/O <BR>函数。打开文件或 设备的所有细节对客户而言都是透明的。 <BR> 4.2BSD支持传送打开描述符,但其实施中有些错误。4.3BSD排除了这些错 <BR>误。3.2 <BR> 及以上版本都支持传送打开描述符。 <BR>下面进一步说明"从一个进程向另一个进程传送一打开文件描述符"的含义。回忆图 <BR>3.3,其中显示了两个进程,它们打开了同一文件。虽然它们共享同一v_node,但每个 <BR>进程都有它自己的文件表项。当从一个进程向另一个进程传送一打开文件描述符时 <BR>,我们想要发送进程和接收进程共享同一文件表项。图15.4示出了所希望的安排。 <BR>在技术上,发送进程实际上向接受进程传送一个指向一打开文件表项的指针。该指 <BR>针被分配存放在接收进程的第一个可用描述符项中。(注意,不要得到错觉以为发送 <BR>进程和接收进程中的描述符编号是相同的,通常它们是不同的。)这种情况与在for <BR>k之后,父、子进程完全共享一个打开文件表项相同(回忆图8.1)。 <BR>当发送进程将描述符传送给接收进程后,通常它关闭该描述符。发送进程关闭该描 <BR>述符并不造成关闭该文件或设备,其原因是该描述符对应的文件仍需为接收进程打 <BR>开(即使接收进程尚未接收到该描述符)。 <BR>图15.4 从上一进程传送一个打开文件至下一进程 <BR>下面我们定义本章使用的三个函数(在第十八章也使用)以发送和接收文件描述符。 <BR>本节将会给出对于SVR4和4.3+BSD的这三个函数的不同实现。 <BR>_______________________________________________________________________ <BR>______ <BR>#include "ourhdr." <BR>int send_fd(int spipefd, int filedes); <BR>int send_err(int spipefd,int status, const char *errmsg); <BR> 两个函数返回:若成功为0,出错为-1 <BR>int recv_fd(int spipefd, ssize_t (*userfunc)(int , const void *, size-t <BR>)); <BR> 返回:若成功为文件描述符,出错<0 <BR>_______________________________________________________________________ <BR>______ <BR>当一个进程(通常是一个服务器)希望将一个描述符传送给另一个进程时,它调用se <BR>nd_fd或send_err。等待接收描述符的进程(客户)调用recv_fd。 <BR>Send_fd经由流管道spipefd发送描述符filedes。send_err 经由流管道spipefd发 <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -