Linux 体系编程 day5 历程管道

[复制链接]
发表于 2025-9-23 00:49:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
历程间通讯(IPC)

Linux环境下,历程所在空间相互独立,任何一个历程的全局变量在另一个历程中都看不到,以是历程和历程之间不能相互访问,要交换数据必须通过内核,在内核中开发一块缓冲区(4096,buf),历程1把数据从用户空间拷贝到内核缓冲区,历程2再从内核缓冲区把数据拷走,内核提供的这种机制称为历程间通讯(IPC)。
常用的历程间通讯的方式有:
1、管道(使用最简单,要求有血缘关系)
2、信号(开销最小)
3、共享映射区(无血缘关系)
4、本地套接字(最稳固)
管道

其本质是一个伪文件(现实上为内核缓冲区)
管道的原理:内核使用环形队列机制,借助内核缓冲区实现。
特质:1、 伪文件  2、管道中的数据只能一次读取  3、数据在管道中只能单向运动  
范围性:1、自己写不能自己读  2、数据不可以反复读取  3、半双工通讯  4、血缘关系历程间可用
pipe函数

创建并打开管道。父子历程共享文件形貌符
  1. int pipe(int fd[2]);
  2. 参数:fd[0]:读端
  3.      fd[1]:写端
  4. 成功返回0
  5. 失败返回-1 errno
复制代码
管道的读写活动 fd[0]读端 、 fd[1]写端
读管道:管道有数据,read返回现实读到的字节数
管道无数据:1、无写端,read返回0(读到文件结尾)2、有写端,read壅闭等待
写管道:无读端,非常停止(由于sigpipe)
有读端:1、管道已满,壅闭等待 2、壅闭未满,返回写出的字节个数
使用管道实现父子间历程通讯,完成ls |wc-l 父历程实现ls , 子历程实现wc-l (末了必要让父历程区实现wc , 子历程实现ls)
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. #include<unistd.h>
  5. #include<pthread.h>
  6. int main(int argc , char *argv[])
  7. {
  8.     pid_t pid;
  9.     int fd[2];
  10.     //char buf[1024];
  11.     int ret;
  12.    //   int a;
  13.     ret = pipe(fd);
  14.     if(ret == -1){
  15.         perror("pipe error");
  16.         exit(1);
  17.     }else if(ret == 0){
  18.         pid = fork();
  19.         if(pid > 0){
  20.             close(fd[0]);
  21.             //execlp("ls" , "ls" , NULL);
  22.             dup2(fd[1] , STDOUT_FILENO);
  23.             execlp("ls" , "ls" , NULL);
  24.             //write(fd[1] , buf , sizeof(buf));
  25.             close(fd[1]);
  26.         }else if(pid ==0){
  27.             close(fd[1]);
  28.             dup2(fd[0] , STDIN_FILENO);
  29.             //ret = read(fd[0] , buf ,sizeof(buf));
  30.             execlp("wc" , "wc" , "-l" , NULL );
  31.             close(fd[0]);
  32.         }
  33.     }
  34.     return 0 ;
  35. }
复制代码
兄弟历程间通讯  fd[0]:读端 fd[1]:写端
  1. int main(int argc , char* argv[])
  2. {
  3.     pid_t pid;
  4.     int i ;
  5.     int ret;
  6.     int fd[2];
  7.     ret = pipe(fd);
  8.     if(ret == -1){
  9.         perror("pipe error");
  10.         exit(1);
  11.     }
  12.     for(i = 0 ; i < 2 ; i++)
  13.     {   
  14.         pid = fork();
  15.         if(pid == 0){
  16.             break;
  17.         }
  18.     }
  19.     if( i == 2 ){        //父进程
  20.         close(fd[0]);
  21.         close(fd[1]);
  22.         wait(NULL);
  23.         wait(NULL);
  24.     }else if(i == 0){    //兄进程  实现ls;
  25.         close(fd[0]);
  26.         dup2(fd[1] , STDIN_FILENO);
  27.         execlp("ls" , "ls" , NULL);
  28.     }else if(i == 1){
  29.         close(fd[1]);
  30.         dup2(fd[0] , STDOUT_FILENO);
  31.         execlp("wc" , "wc" , "-l" , NULL);
  32.     }
  33. }
复制代码
管道的优点:简单,相比信号,套接字时间历程间通讯简单很多。
缺点:只能单向通讯,双向通讯必要创建两个管道  。 只能用于父子、兄弟历程间通讯。
FIFO

定名管道,makefifo 管道名
  1. int mkfifo("管道名" , 权限/0644)
复制代码
共享存储映射、存储映射I/O

mmap函数

创建共享内存映射 , 必要包罗头文件#include<sys/mman.h>
  1. 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函数

开释映射区
  1. int munmap(void *addr , size_t length)
复制代码
参数:addr : mmap的返回值 。 length 大小
返回值:乐成返回0,失败返回-1
mmap 和 munmap函数使用实例:
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<fcntl.h>
  4. #include<string.h>
  5. #include<sys/mman.h>
  6. int main(int argc , char*argv[])
  7. {   
  8.    
  9.     char*p = NULL;
  10.     int fd ;
  11.     fd = open("textmmap" , O_WRDR|O_CREAT|O_TRUNC , 0644);
  12.     lseek(fd , 20 , SEEL_END);
  13.     //引起文件IO操作
  14.     write(fd , "\0" , 1);
  15.     int len = lseek(fd , 0 , SEEK_END);
  16.     p = mmap(NULL, len , PROT_READ|PROT_WRITE , MAP_SHARED , fd , 0);
  17.     if(p == MAP_FAILED){
  18.         perror("mmap error");
  19.         exit(1);
  20.     }
  21.     //接着可以使用对指针的操作对文件进行操作
  22.     strcpy(p , "hello mmap\n") ;
  23.     printf("-----%s" , p);
  24.     //清空文件映射区
  25.     int ret = munmap( p , len);
  26.     if(ret == -1){
  27.         perror("munmap error");
  28.         exit(1);
  29.     }
  30.     close(fd);
  31.     return 0;
  32. }
复制代码
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

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表