《APUE》读书笔记-第十七章高级进程间通信
本章主要介绍了基于STREAM的管道和UNIX域套接字,这些IPC可以在进程间传送打开文件描述符。服务进程可以使用它们的打开文件描述符与指定的名字相关联,客户进程可以使用这些名字与服务器进程通信。
1、基于STREAMS的管道
STREAMS pipe是一个双向(全双工)管道,单个STREAMS管道就能向父、子进程提供双向的数据流。如下图所示:

下面采用STREAMS管道实现加法协同进程实例,程序如下:
View Code
1 1 #include <stdio.h>
2 2 #include <stdlib.h>
3 3 #include <unistd.h>
4 4 #include <errno.h>
5 5 #include <string.h>
6 6 #include <signal.h>
7 7
8 8 #define MAXLINE 1024
9 9
10 10 static void sig_pipe(int signo)
11 11 {
12 12 printf("SIGPIPE caught\n");
13 13 exit(1);
14 14 }
15 15 int s_pipe(int fd[2])
16 16 {
17 17 return pipe(fd);
18 18 }
19 19 int main()
20 20 {
21 21 int n;
22 22 int fd[2];
23 23 pid_t pid;
24 24 char line[MAXLINE];
25 25
26 26 signal(SIGPIPE,sig_pipe);
27 27 s_pipe(fd);
28 28 if((pid = fork()) == -1)
29 29 {
30 30 perror("fork() error");
31 31 exit(-1);
32 32 }
33 33 if(pid == 0) //子进程用fd[1]
34 34 {
35 35 close(fd[0]);
36 36 dup2(fd[1],STDIN_FILENO);
37 37 dup2(fd[1],STDOUT_FILENO);
38 38 execl(".//add","add",(char *)0);
39 39 exit(0);
40 40 }
41 41 else //父进程用fd[0]
42 42 {
43 43 close(fd[1]);
44 44 while(fgets(line,MAXLINE,stdin) != NULL)
45 45 {
46 46 n = strlen(line);
47 47 if(write(fd[0],line,n) != n)
48 48 {
49 49 perror("write() error to pipe");
50 50 exit(-1);
51 51 }
52 52 if((n =read(fd[0],line,MAXLINE)) < 0)
53 53 {
54 54 perror("read() error to pipe");
55 55 exit(-1);
56 56 }
57 57 if(n==0)
58 58 {
59 59 printf("child close pipe\n");
60 60 break;
61 61 }
62 62 line[n] = '\0';
63 63 fputs(line,stdout);
64 64 }
65 65 exit(0);
66 66 }
67 67 }
在APUE2 P469中这样讲:“solaris支持STREAMS管道,Linux的可选附加包也提供了STREAMS管道。”这个包没有安装上,程序不能正常运行。
1.2命名的STREAMS管道
管道仅在相关进程之间使用,例如子进程集成父进程的管道。无关进程可以使用FIFO进行通信,但是这仅仅提供单向通信。STREAMS机制提供了一种有效的途径,使得进程可以给予管道一个文件系统的名字,避免了单向FIFO的问题。操作函数原型如下:
#include <stropts.h>
int fattach(int fildes, const char *path); //给STREAMS管道一个系统文件中的名字
int fdetach(const char *path); //撤销STREAMS管道文件与文件系统中名字的关联
2、UNIX域套接字
UNIX域套接字用于在同一台机器上运行的进程之间的通信,UNIX域套接字仅仅复制数据,不执行协议处理,不需要添加或删除网络报头,无需计算校验和,不要产生顺序号,无需发送确认报文。UNIX域套接字是套接字和管道之间的结合物,提供流和数据报两种接口。
UINX域套接字的好处:
(1)在同一台主机上进行通信时,是不同主机间通信的两倍
(2)UINX域套接口可以在同一台主机上,不同进程之间传递套接字描述符
(3)UINX域套接字可以向服务器提供客户的凭证(用户id或者用户组id)
UINX域套接字使用的地址通常是文件系统中一个文件路径,这些文件不是不同的文件,只能作为域套接字通信,不能读写。创建函数如下:
#include <sys/socket.h>
int socketpair(int domain, int type, int protocolint " sv [2]);
利用UNIX域实现全双工管道,程序如下:
View Code
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <signal.h>
7 #include <sys/socket.h>
8 #define MAXLINE 1024
9
10 static void sig_pipe(int signo)
11 {
12 printf("SIGPIPE caught\n");
13 exit(1);
14 }
15 int s_pipe(int fd[2])
16 {
17 return socketpair(AF_UNIX,SOCK_STREAM,0,fd); //创建UNIX域套接字
18 }
19 int main()
20 {
21 int n;
22 int fd[2];
23 pid_t pid;
24 char line[MAXLINE];
25
26 signal(SIGPIPE,sig_pipe);
27 s_pipe(fd);
28 if((pid = fork()) == -1)
29 {
30 perror("fork() error");
31 exit(-1);
32 }
33 if(pid == 0)
34 {
35 close(fd[0]);
36 dup2(fd[1],STDIN_FILENO);
37 dup2(fd[1],STDOUT_FILENO);
38 execl(".//add","add",(char *)0);
39 exit(0);
40 }
41 else
42 {
43 close(fd[1]);
44 while(fgets(line,MAXLINE,stdin) != NULL)
45 {
46 n = strlen(line);
47 if(write(fd[0],line,n) != n)
48 {
49 perror("write() error to pipe");
50 exit(-1);
51 }
52 if((n =read(fd[0],line,MAXLINE)) < 0)
53 {
54 perror("read() error to pipe");
55 exit(-1);
56 }
57 if(n==0)
58 {
59 printf("child close pipe\n");
60 break;
61 }
62 line[n] = '\0';
63 fputs(line,stdout);
64 }
65 exit(0);
66 }
67 }
add是单独的程序,共程序调用,执行结果如下:

2.1命令UNIX域套接字
UNIX域套接字的地址有sockaddr_run结构表示。
#define UNIX_PATH_MAX 108
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname };
写个程序,将一个地址绑定一UNIX域套接字,程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stddef.h>
int main()
{
int fd,size;
struct sockaddr_un un;
un.sun_family = AF_UNIX;
strcpy(un.sun_path,"foo.socket");
fd = socket(AF_UNIX,SOCK_STREAM,0);
size = offsetof(struct sockaddr_un,sun_path) + strlen(un.sun_path);
if(bind(fd,(struct sockaddr*)&un,size)<0)
{
perror("bind() error");
exit(-1);
}
printf("UNIX domain socket bound\n");
exit(0);
}
程序执行结果如下:

从结果可以看出,如果绑定地址时候,文件已经存在,那么bind请求将会失败,关闭套接字时,并不删除该文件。因此必须保证在应用程序终止前,对该文件进行解除链接操作。
2.2唯一连接
服务器进程可以使用标准bind、listen和accept函数为客户进程安排一个唯一的UNIX域连接,客户进程使用connect与服务器进程联系,服务器进程接受了connect请求后,在服务器进程和客户进程之间就存在了唯一的连接。程序参考http://blog.csdn.net/youkuxiaobin/article/details/6965527 http://bbs.chinaunix.net/thread-2183106-1-1.html

View Code
浙公网安备 33010602011771号