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

下面采用STREAMS管道实现加法协同进程实例,程序如下:
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域实现全双工管道,程序如下:
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
unix域套接字
unix域套接字实际上不是一个实际的协议,他只是在同一台主机上客户和服务器之间通信时,使用与在不同主机上客户和服务器间通信时相同的API
unix域套接字分为两种:字节流套接字和数据报套接字
unix域套接字的好处:
1 在同一台主机上进行通信时,是不同主机间通信的两倍
2 unix域套接口可以在同一台主机上,不同进程之间传递套接字描述符
3 unix域套接字可以向服务器提供客户的凭证(用户id或者用户组id)
unix域套接字使用的地址通常是文件系统中一个文件路径(套接口文件:APUE中的4.3节文件类型,是以s开头的),这些文件不是不同的文件,只能作为域套接字通信,不能读写

并且是以s开头的文件
unix域套接字的地址结构是:
- struct sockaddr_un
- {
- uint8_t sun_len;
- sa_family_sun_family;//AF_LOCAL
- char sun_path[104];//必须是以空结尾的字符串(路径+文件名)
- }
int socketpair(int family, int type, intprotocol, int sockfd[2]);
这个函数创建两个互相连接的套接字(socketfd[2])family 是AF_LOCAL, type可以是SOCK_STREAM (字节流)或者SOCK_DGRAM(数据报),协议是0,之后就能够或者两个互相连接的套接字
以SOCK_STREAM调用的socketpair函数得到的套接字叫做流管道(stream pipe),是全双工的,就是这两个套接字是可读可写的
※
1在unix域套接字进行bind的时候建立套接口文件,其默认的权限值是0777,并被当前的umask修改,看上图就知道,umask是0022 的到的文件权限是0755
2关于bind创建文件中地址参数 sockaddr_un 中的sun_path需要是绝对路径,这样才能不用考虑相对的概念。防止客户端程序也用相对路径,但是和服务器不在同一个目录的考虑
3 connect中的地址中的路径名必须是套接口文件,而且已经被服务端绑定的,以下情况会出错:1 文件路径存在但是不是套接口文件2 路径名存在且是套接口文件,但是没有和该文件绑定的套接口3 就是type必须和服务器相同
4 connect连接unix套接口的时候的权限检查和open函数一样的
5 unix域字节流套接口和TCP一样都给进程提供一个无记录边界的字节流接口
6 unix域套接口connect发现等待队列满了,就直接返回ECONNREFUSRD错误,说连接拒绝错误
7 unix域数据报套接口和UDP一样提供一个保留记录边界的不可靠数据报服务
unix域套接字的客户端:
- #include <sys/un.h>
- #include <stdio.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #define MAX_SEND 1025
- #define UNIX_PATH "/tmp/sinfor"
- void dump_unix(int sock_fd)
- {
- char tmp[MAX_SEND] = {0};
- char recv[MAX_SEND] = {0};
- while(fgets(tmp, MAX_SEND, stdin) != NULL)
- {
- write(sock_fd, tmp, strlen(tmp));
- read(sock_fd, recv, MAX_SEND);
- printf("data : %s\n", recv);
- bzero(tmp,MAX_SEND);
- bzero(recv, MAX_SEND);
- }
- }
- int main(int argc, char** argv)
- {
- int conn_sock = socket(AF_LOCAL, SOCK_STREAM, 0);
- if(conn_sock == -1)
- {
- perror("socket fail ");
- return -1;
- }
- struct sockaddr_un addr;
- bzero(&addr, sizeof(addr));
- addr.sun_family = AF_LOCAL;
- strcpy((void*)&addr.sun_path, UNIX_PATH);
- if(connect(conn_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
- {
- perror("connect fail ");
- return -1;
- }
- dump_unix(conn_sock);
- close(conn_sock);
- return 0;
- }
unix域套接字的服务器;
- #include <sys/un.h>
- #include <stdio.h>
- #include <signal.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/socket.h>
- #define MAX_RECV 1025
- #define UNIX_SERV_PATH "/tmp/sinfor"
- void client_dump(int sock_fd)
- {
- char rec[MAX_RECV] = {0};
- int size;
- while((size = read(sock_fd, rec, MAX_RECV)) != 0)
- {
- printf("**********1111************\n");
- printf("recv data is %s\n", rec);
- write(sock_fd, rec, size);
- }
- }
- void sig_son(int num)
- {
- printf("son is %d\n", getpid());
- wait(NULL);
- //return NULL;
- }
- int main(int argc, char** argv)
- {
- int acc_sock, dump_sock;
- acc_sock = socket(AF_LOCAL, SOCK_STREAM, 0);
- if(acc_sock == -1)
- {
- perror("socket func fail ");
- return -1;
- }
- struct sockaddr_un ser_addr, cli_addr;
- ser_addr.sun_family = AF_LOCAL;
- strcpy(ser_addr.sun_path, UNIX_SERV_PATH);
- unlink(UNIX_SERV_PATH);
- bind(acc_sock, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
- listen(acc_sock, 5);
- signal(SIGCHLD, sig_son);
- while(1)
- {
- int len = sizeof(cli_addr);
- dump_sock = accept(acc_sock, (struct sockaddr*)&cli_addr, &len);
- if(dump_sock == -1)
- {
- if(errno == EINTR)
- {
- continue;
- }
- else
- {
- perror("accept fail");
- return -1;
- }
- }
- if(fork() == 0)
- {
- close(acc_sock);
- client_dump(dump_sock);
- close(dump_sock);
- exit(0);
- }
- close(dump_sock);
- }
- close(acc_sock);
- return 0;
- }
- /*
- domain_socket.h
- @Author: duanjigang @2006-4-11
- @Desp: declaratin of methods used for unix-domain-socket communication
- */
- #ifndef _H_
- #define _H_
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/un.h>
- #include <sys/socket.h>
- #define MSG_SIZE 1024
- int init_send_socket(struct sockaddr_un * addr,char * path)
- {
- int sockfd,len;
- sockfd=socket(AF_UNIX,SOCK_DGRAM,0);
- if(sockfd<0)
- {
- exit(1);
- }
- bzero(addr,sizeof(struct sockaddr_un));
- addr->sun_family=AF_UNIX;
- strcpy(addr->sun_path,path);
- return sockfd;
- }
- int init_recv_socket(char * path)
- {
- int sockfd,len;
- struct sockaddr_un addr;
- sockfd=socket(AF_UNIX,SOCK_DGRAM,0);
- if(sockfd<0)
- {
- return -1;
- }
- bzero(&addr,sizeof(struct sockaddr_un));
- addr.sun_family = AF_UNIX;
- strcpy(addr.sun_path, path);
- unlink(path);
- len = strlen(addr.sun_path) + sizeof(addr.sun_family);
- if(bind(sockfd,(struct sockaddr *)&addr,len)<0)
- {
- return -1;
- }
- return sockfd;
- }
- int receive_from_socket(int sockfd, char msg[])
- {
- int n;
- memset(msg, 0, MSG_SIZE);
- n=recvfrom(sockfd, msg, MSG_SIZE, 0, NULL, NULL);
- if(n<=0)
- {
- return -1;
- }
- msg[n]=0;
- return n;
- }
- int send_to_socket(int sockfd, char msg[], const struct sockaddr_un * addr)
- {
- int len;
- len = strlen(addr->sun_path)+sizeof(addr->sun_family);
- sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)addr,len);
- return 1;
- }
- #endif
- /*
- main.c
- @Author: duanjigang @ 2006-4-11
- @Desp: Two processes communicate with unix domain socket
- */
- #include "domain_socket.h"
- #define PATH "/home/useless"
- /*
- 进程间通过域进行通讯-举例:父子进程,一个发送,一个接收
- */
- int main(void)
- {
- int pid;
- /*
- 子进程用于发送消息
- */
- if((pid = fork()) == 0)
- {
- int fd, counter = 0;
- char send_buffer[MSG_SIZE];
- struct sockaddr_un addr;
- if( (fd = init_send_socket(&addr, PATH)) > 0)
- while(1)
- {
- memset(send_buffer, 0 , MSG_SIZE);
- /*
- 防止计数器越界,所以做一个复位判断
- */
- sprintf(send_buffer,"message for %d times",
- counter++ >= 10000 ? 1 : counter);
- send_to_socket(fd, send_buffer, &addr);
- printf("Sender: %s\n", send_buffer);
- sleep(1);
- }
- }/*
- 父进程用于接收消息
- */
- else
- {
- int fd;
- char recv_buffer[MSG_SIZE];
- if( (fd = init_recv_socket(PATH))> 0)
- while(1)
- {
- memset(recv_buffer, 0, MSG_SIZE);
- if(receive_from_socket(fd, recv_buffer))
- {
- printf("Receiver: %s\n", recv_buffer);
- }
- }
- }
- }
Unix环境高级编程(十八)高级进程间通信的更多相关文章
- Unix环境高级编程(十五)高级I/O
1.非阻塞I/O 对低速设备的I/O操作可能会使进程永久阻塞,这类系统调用主要有如下情况:(1)如果数据并不存在,则读文件可能会使调用者永远阻塞(例如读管道.终端设备和网络设备).(2)如果数据不能立 ...
- (十一) 一起学 Unix 环境高级编程 (APUE) 之 高级 IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- Unix环境高级编程(十六)进程间通信
进程间通信(IPC)是指能在两个进程间进行数据交换的机制.现代OS都对进程有保护机制,因此两个进程不能直接交换数据,必须通过一定机制来完成. IPC的机制的作用: (1)一个软件也能更容易跟第三方软件 ...
- Unix环境高级编程(十二)线程控制
本章介绍了一个进程中多个线程之间如何保持数据的似有性及进程的系统调用如何与线程进行交互. 1.线程限制: Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询 ...
- Unix环境高级编程(十)信号续
1.signal函数 Unix系统的信号机制最简单的接口是signal函数,函数原型如下: #include <signal.h> typedef void (*sighandler_t) ...
- Unix环境高级编程(十九)终端I/O
终端I/O应用很广泛,用于终端.计算机之间的直接连线.调制解调器以及打印机等等.终端I/O有两种不同的工作模式: (1)规范模式输入处理:终端输入以行为单位进行处理,对于每个读要求,终端驱动程序最多返 ...
- Unix环境高级编程(十四)守护进程实现时间服务器
守护进程是在后台运行不受终端控制的进程(如输入.输出等),一般的网络服务都是以守护进程的方式运行.守护进程脱离终端的主要原因有两点:(1)用来启动守护进程的终端在启动守护进程之后,需要执行其他任务.( ...
- UNIX环境高级编程笔记之高级I/O
本章说明了很多高级I/O功能: 非阻塞I/O——发一个I/O操作,不使其阻塞,记录锁,STREAMS机制 I/O多路转接——select和poll函数 readv和writev函数,以及存储映射I/O ...
- Swift5 语言指南(二十八) 高级运算符
除了Basic Operators中描述的运算符之外,Swift还提供了几个执行更复杂值操作的高级运算符.这些包括C和Objective-C中您熟悉的所有按位和位移运算符. 与C中的算术运算符不同,S ...
随机推荐
- IOS UITableView多选删除功能
UITbableView作为列表展示信息,除了展示的功能,有时还会用到删除,比如购物车.收藏列表等. 单行删除功能可以直接使用系统自带的删除功能,当横向轻扫cell时,右侧出现红色的删除按钮,点击删除 ...
- 【系统】supervisor支持多进程
[program:deployworker] directory = /etc/ansible/easyAnsible/app/deploy/ command = python Deploy.py p ...
- ListView GridViewColumn.CellTemplate
<ListView.View> <GridView> <GridViewColumn Header="Meaningful Use Objectives&quo ...
- Hyper-V如何应用新的网卡
最近新装了块网卡,可是在Hyper-V的虚拟机设置里怎么也找不到如何应用这个新网卡. 把我郁闷坏了. 偶尔点点,才发现原来不是在虚拟机的设置里面,而是在上面的一级设置. 新建完后就可以在虚拟机的网 ...
- [CSS] Pseduo
#self aside li{ list-style-type: none;padding:5px;border-bottom: 1px solid #ccc;} #self aside li:las ...
- Memcached 的一些用法
public interface ICache { object Get(string key); /// <summary> /// 根据 key 从缓存中读取数据 /// </s ...
- T-SQL 之 事务
事务全部是关于原子性的.原子性是指可以把一些事情当做一个单元来看待.从数据库的角度看,它是指应全部执行或全部都不执行的一条或多条语句的最小组合. 事务要有非常明确的开始和结束点.SQL Server中 ...
- 上传app store 应用指南链接--2
http://www.docin.com/p-166876874.html http://jamesli.cn/blog/?p=955 http://www.adobe.com/devnet/flas ...
- PHPWIND和DISCUZ什么区别?
大家都知道,phpwind和discuz是两款用来做论坛的强大的php开源程序.客观的说,现在phpwind和discuz各有所长,要说他们哪个比较好,也是很难的事情,无忧主机小编在这里也不敢妄下结论 ...
- SpringMVC的入门例子
MVC框架是什么 模型-视图-控制器(MVC)是一个众所周知的以设计界面应用程序为基础的设计模式.它主要通过分离模型.视图及控制器在应用程序中的角色将业务逻辑从界面中解耦.通常,模型负责封装应用程序数 ...