马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
历程间通讯(IPC)
Linux环境下,历程所在空间相互独立,任何一个历程的全局变量在另一个历程中都看不到,以是历程和历程之间不能相互访问,要交换数据必须通过内核,在内核中开发一块缓冲区(4096,buf),历程1把数据从用户空间拷贝到内核缓冲区,历程2再从内核缓冲区把数据拷走,内核提供的这种机制称为历程间通讯(IPC)。
常用的历程间通讯的方式有:
1、管道(使用最简单,要求有血缘关系)
2、信号(开销最小)
3、共享映射区(无血缘关系)
4、本地套接字(最稳固)
管道
其本质是一个伪文件(现实上为内核缓冲区)
管道的原理:内核使用环形队列机制,借助内核缓冲区实现。
特质:1、 伪文件 2、管道中的数据只能一次读取 3、数据在管道中只能单向运动
范围性:1、自己写不能自己读 2、数据不可以反复读取 3、半双工通讯 4、血缘关系历程间可用
pipe函数
创建并打开管道。父子历程共享文件形貌符- int pipe(int fd[2]);
- 参数:fd[0]:读端
- fd[1]:写端
- 成功返回0
- 失败返回-1 errno
复制代码 管道的读写活动 fd[0]读端 、 fd[1]写端
读管道:管道有数据,read返回现实读到的字节数
管道无数据:1、无写端,read返回0(读到文件结尾)2、有写端,read壅闭等待
写管道:无读端,非常停止(由于sigpipe)
有读端:1、管道已满,壅闭等待 2、壅闭未满,返回写出的字节个数
使用管道实现父子间历程通讯,完成ls |wc-l 父历程实现ls , 子历程实现wc-l (末了必要让父历程区实现wc , 子历程实现ls)- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- #include<unistd.h>
- #include<pthread.h>
- int main(int argc , char *argv[])
- {
- pid_t pid;
- int fd[2];
- //char buf[1024];
- int ret;
- // int a;
- ret = pipe(fd);
- if(ret == -1){
- perror("pipe error");
- exit(1);
- }else if(ret == 0){
- pid = fork();
- if(pid > 0){
- close(fd[0]);
- //execlp("ls" , "ls" , NULL);
- dup2(fd[1] , STDOUT_FILENO);
- execlp("ls" , "ls" , NULL);
- //write(fd[1] , buf , sizeof(buf));
- close(fd[1]);
- }else if(pid ==0){
- close(fd[1]);
- dup2(fd[0] , STDIN_FILENO);
- //ret = read(fd[0] , buf ,sizeof(buf));
- execlp("wc" , "wc" , "-l" , NULL );
- close(fd[0]);
- }
- }
- return 0 ;
- }
复制代码 兄弟历程间通讯 fd[0]:读端 fd[1]:写端- int main(int argc , char* argv[])
- {
- pid_t pid;
- int i ;
- int ret;
- int fd[2];
- ret = pipe(fd);
- if(ret == -1){
- perror("pipe error");
- exit(1);
- }
- for(i = 0 ; i < 2 ; i++)
- {
- pid = fork();
- if(pid == 0){
- break;
- }
- }
- if( i == 2 ){ //父进程
- close(fd[0]);
- close(fd[1]);
- wait(NULL);
- wait(NULL);
- }else if(i == 0){ //兄进程 实现ls;
- close(fd[0]);
- dup2(fd[1] , STDIN_FILENO);
- execlp("ls" , "ls" , NULL);
- }else if(i == 1){
- close(fd[1]);
- dup2(fd[0] , STDOUT_FILENO);
- execlp("wc" , "wc" , "-l" , NULL);
- }
- }
复制代码 管道的优点:简单,相比信号,套接字时间历程间通讯简单很多。
缺点:只能单向通讯,双向通讯必要创建两个管道 。 只能用于父子、兄弟历程间通讯。
FIFO
定名管道,makefifo 管道名- int mkfifo("管道名" , 权限/0644)
复制代码 共享存储映射、存储映射I/O
mmap函数
创建共享内存映射 , 必要包罗头文件#include<sys/mman.h>- void *mmap(void*addr , size_t length , int prot , int flags, int fd , off_t offset);
复制代码 参数:addr :指定映射区的首所在 , 通常传NULL,体现让体系自动分配。
length:共享内存映射区的大小 (< = 文件现实大小)
prot:共享内存映射区的读写属性 PROT_READ、PROT_WRITE 、 |。
flags:标注共享内存的共享属性。MAP_SHARED、 MAP_PRIVATE
fd:用于创建共享映射区的谁人文件的文件形貌符
offset :默认0,体现映射文件全部。偏移位置。4K的整数倍。
返回值:
乐成:返回映射区的首所在。
失败:MAP_FAILED(void*(-1)) 设置errno。map_failed
munmap函数
开释映射区- int munmap(void *addr , size_t length)
复制代码 参数:addr : mmap的返回值 。 length 大小
返回值:乐成返回0,失败返回-1
mmap 和 munmap函数使用实例:- #include<stdio.h>
- #include<stdlib.h>
- #include<fcntl.h>
- #include<string.h>
- #include<sys/mman.h>
- int main(int argc , char*argv[])
- {
-
- char*p = NULL;
- int fd ;
- fd = open("textmmap" , O_WRDR|O_CREAT|O_TRUNC , 0644);
- lseek(fd , 20 , SEEL_END);
- //引起文件IO操作
- write(fd , "\0" , 1);
- int len = lseek(fd , 0 , SEEK_END);
- p = mmap(NULL, len , PROT_READ|PROT_WRITE , MAP_SHARED , fd , 0);
- if(p == MAP_FAILED){
- perror("mmap error");
- exit(1);
- }
- //接着可以使用对指针的操作对文件进行操作
- strcpy(p , "hello mmap\n") ;
- printf("-----%s" , p);
- //清空文件映射区
- int ret = munmap( p , len);
- if(ret == -1){
- perror("munmap error");
- exit(1);
- }
- close(fd);
- return 0;
- }
复制代码 mmap的注意事项
1、用于创建映射区的文件大小为0,现实指定非0 大小创建映射区,会出现总线错误。
2、用于创建映射区的文件大小为0,现实指定 0 大小创建映射区,会出现错误(无效参数)。
3、用于创建映射区的文件读写属性为只读,映射区属性为读写,会出现错误(无效参数)。
4、创建映射区必要read权限,当访问权限指定为MAP_SHARED时, mmap的读写权限应该<=文件的open权限。分析mmap必须要有读权限,只有写是不行的。
5、文件形貌符fd在mmap创建映射区完成即可关闭,后续访问文件,用所在访问。
6、offset必须是4096的整数倍。(mmu映射的最小单位是4k)。
7、对申请的映射区内存不能越界访问。
8、munmap用于开释的所在,必须要是mmap返回的所在。
9、映射区的访问权限为私有MAP_PRIVATE , 对内存所做修改,只在内存有用,不会返回到物理磁盘上。
mmap函数的保险调用方式:
1、open的时间指定O_RDWR;
2、mmap(NULL , 有用文件大小 , PROT_WRITE|PROT_READ , MAP_SHARED , fd , 0);
总结:mmap创建映射区堕落概率非常高,肯定要查抄返回值 MAP_FAILED
|