1.
进程间通信概述
一个大型的应用系统,往往需要众多进程协作,进程间通信的重要性就显而易见。在早期,UNIX系统IPC就是进程间通信方式的统称。进程间通信就是可以让多个进程可以互相访问,包括程序运行的实时数据,也包括对方的代码段。
2.
进程间通信的难点
进程运行期间,其地址空间对于其他进程是不可见的(这是传统上的进程概念,IPC共享内存机制打破了这个概念),在系统中他们是相对独立的,并不能互相访问对方。
Linux/UNIX系统提供了一种中间转发的机制,为多个进程建立起互相通信的数据通道。
两个进程通过IPC机制来转发数据。
3.
IPC的多种方式
IPC是进程间通信的统称,包括以下类型:
半双工通道:匿名半双工通道
+ FIFO(命名半双工管道)
全双工通道:匿名全双工 + 命名全双工
System V IPC/
POSIX IPC:消息队列 信号量
共享存储
网络进程间通信:SOCKET STREAMS
4.
管道
管道,是在两个进程之间实现一个数据流通的管道,该管道可以是单向或双向的。优点是简单易用,缺点是功能简单,有很多限制。
(1)
管道的概念
管道实现数据以数据流的形式在多进程间流动。在系统中相当于文件系统上的一个文件,来缓存所要传输的数据。在某些特性上又不同于文件,比如,当数据读出后,管道中就没有数据了。
匿名半双工管道在系统中是没有实名的,并不可以在文件系统中以任何方式看到该管道。它只是进程的一种资源,会随着进程的结束而被系统清除。管道通信的使用比较常见,比如grep命令。
#ls | grep tftp
这个命令使用的就是半双工管道,grep命令的输入是ls命令的输出。管道从数据流动方向上又分全双工管道+半双工管道。
(2)
匿名半双工管道
匿名管道没有名字,对于管道中使用的文件描述符没有路径名,也就是或不存在任何意义上的文件。它们只是在内存中跟某一个索引节点相关联的两个文件描述符。
匿名半双工管道的特性如下:
数据只能在一个方向上流动。
只能在具有公共祖先的进程间通信,即或是父子进程或是兄弟进程间通信
Linux环境下使用pipe函数创建一个半双工管道
#include
int pipe(int fd[2]);
参数fd[2]是一个长度为2的文件描述符数组,fd[0]是读出端,fd[1]是写入端;函数的返回值为0表示成功,-1表示失败。当函数成功返回时,自动维护了一个从fd[1]àfd[0]的数据通道。
示例:
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- int main(void)
- {
- int fd[2];
- char str[256];
-
- if((pipe(fd))<0){
- perror("pipe");
- exit(1);
- }
-
- write(fd[1],"create pipe successfully !\n",31);
-
- read(fd[0],str,sizeof(str));
- printf("%s",str);
-
- printf("pipe file descriptors are %d,%d\n",fd[0],fd[1]);
-
- close(fd[0]);
- close(fd[1]);
-
- return 0;
- }
(3)
匿名半双工通道的读写操作
使用read和write函数对管道进行读写操作。当对一个读端已经关闭的管道进行写操作时,会产生SIGPIPE,说明管道的读端已经关闭,并且write操作返回-1,errno的值为EPIPE,对于SIGPIPE信号可以进行捕捉处理。如果写入进程不能捕捉或者忽略SIGPIPE信号,则写入进程会中断。
如果要建立一个父进程到子进程的数据通道,可以先调用pipe函数紧接着调用fork函数,由于子进程自动继承父进程的数据段,则父子进程同时拥有管道的操作权,此时管道的方向取决于用户怎么维护管道。
当用户想要一个父进程到子进程的数据通道时,可以在父进程中关闭管道的读出端,在子进程中关闭管道的输入端;相反,当维护子进程到父进程的数据通道时,在父进程中关闭输出,在子进程中关闭读入。使用pipe()和fork()组合,可以构造出所有的父进程与子进程或子进程到兄弟进程的管道。
示例1:父子进程间
- #include <unistd.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #define BUFS PIPE_BUF //PIPE_BUF,管道默认一次性读写的数据长度
- int main(void)
- {
- int fd[2];
- char str[256];
- pid_t pid;
- int len;
-
-
- if((pipe(fd))<0){
- perror("pipe");
- exit(1);
- }
-
- if((pid=fork())<0)
- {
- perror("failed to create pipe\n");
- exit(1);
- }else if((pid=fork())>0)
- { //parent process
- close(fd[0]);
- write(fd[1],"hello pipe\n",13);
- exit(0);
- }else{//child process
- close(fd[1]);
- len = read(fd[0],buf,BUFS);
-
- if(len<0)
- {
- perror("read pipe fail\n");
- exit(0);
- }
- else
- write(STDOUT_FILENO,buf,len);//将管道中的输出到标准输出
-
- exit(0);
- }
-
- }
示例2:兄弟进程间
- #include <unistd.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #define BUFS PIPE_BUF //PIPE_BUF管道默认一次性读写的数据长度
- int main(void)
- {
- int fd[2];
- char str[256];
- pid_t pid;
- int len;
-
-
- if((pipe(fd))<0){
- perror("pipe");
- exit(1);
- }
-
- if((pid=fork())<0)
- {
- perror("failed to create pipe\n");
- exit(1);
- }else if((pid=fork())==0)
- {
- close(fd[0]);
- write(fd[1],"hello my brother\n");
- exit(0);
- }
-
- if((pid=fork())<0)
- {
- perror("failed to create pipe\n");
- exit(1);
- }else
- if((pid=fork())>0)
- { //parent close fd[0],fd[1],放弃管道操作权,不对管道进行任何操作。
- close(fd[0]);
- close(fd[1]);
- exit(0);
- }else
- {//brother process
- close(fd[1]);//放弃写操作权
- len = read(fd[0],buf,BUFS);
-
- if(len<0)
- {
- perror("read pipe fail\n");
- exit(0);
- }
- else
- write(STDOUT_FILENO,buf,len);//将管道中的输出到标准输出
-
- exit(0);
- }
- }
阅读(1270) | 评论(0) | 转发(2) |