做网站编辑前景谷歌浏览器在线入口
目录
- 1.理解层面
- 1.1为什么要进程间通信??
- 1.2什么是通信??
- 1.3怎么通信??
- 1.4 具体通信的方式??
- 2.管道
- 3.匿名管道
- 3.1 用fork来共享管道
- 3.2 特性
- 3.3 通信情况
- 4.命名管道
- 4.1 创建一个命名管道
- 4.2 匿名管道和命名管道的区别
- 5.System V 共享内存
- 5.1 共享内存函数
1.理解层面
1.1为什么要进程间通信??
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它发生了某种事情(如进程终止时要通知父进程)
- 进程控制:有些进程希望完全控制另一个进程的执行(Debug进程)
1.2什么是通信??
进程间通信是指在计算机系统中,运行在不同进程之间的数据交换和通信机制。由于每个进程都有自己的独立内存空间,因此进程间通信需要通过特定的机制来实现数据的共享和传递
1.3怎么通信??
进程间通信的本质:是先让不同的进程,先看到同一份资源[“内存”]
1.4 具体通信的方式??
- 基于文件的,管道通信
- 匿名管道pipe
- 命名管道
- System V ,本机通信
- System V 消息队列
- System V 共享内存
- System V 信号量
2.管道
什么是管道??
- 管道是Unix中最古老的进程间通信的形式
- 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
3.匿名管道
#include<unistd.h>
int pipe(int pipefd[2]);
参数:pipefd:文件描述符数组,其中pipefd[0]表示读端,pipefd[1]表示写端返回值:成功返回0,失败返回-1
//列子:查看pipefd对应的文件描述符
//testPipe.cc#include <iostream>
#include <unistd.h>int main()
{//1.创建管道int pipefd[2]={0};//pipefd[0] 读端 pipefd[1] 写端int n=pipe(pipefd);if(n<0){perror("pipe fail");exit(1);}std::cout<<"pipefd[0]: "<<pipefd[0]<<std::endl;std::cout<<"pipefd[1]: "<<pipefd[1]<<std::endl;
}//运行结果:
$ ./testPipe
pipefd[0]: 3
pipefd[1]: 4
3.1 用fork来共享管道
//测试样例:测试管道的读写
//让父进程读,子进程写
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>void ChildWrite(int wfd)
{char buff[1024];int cnt=0;while(true){snprintf(buff,sizeof(buff),"I am child,my pid:%d,cnt: %d\n",getpid(),cnt++);write(wfd,buff,strlen(buff));sleep(1);}
}
void FatherRead(int rfd)
{char buff[1024];while(true){buff[0]=0;ssize_t n=read(rfd,buff,sizeof(buff)-1);if(n>0){buff[n]=0;std::cout<<"father say: "<<buff<<std::endl;}sleep(1);}
}int main()
{// 1.创建管道int pipefd[2] = {0}; // pipefd[0] 读端 pipefd[1] 写端int n = pipe(pipefd);if (n < 0){perror("pipe fail");exit(1);}std::cout << "pipefd[0]: " << pipefd[0] << std::endl;std::cout << "pipefd[1]: " << pipefd[1] << std::endl;// 2.创建子进程pid_t id =fork();if(id<0){perror("fork fail");exit(1);}else if(id==0){//child//3.关闭不需要的读端,形成通信close(pipefd[0]);ChildWrite(pipefd[1]);close(pipefd[1]);exit(0);}else{//father//3.关闭不需要的写端,形成通信close(pipefd[1]);FatherRead(pipefd[0]);close(pipefd[0]);}
}//查看运行结果:
$ ./testPipe
pipefd[0]: 3
pipefd[1]: 4
father say: I am child,my pid:1670938,cnt: 0father say: I am child,my pid:1670938,cnt: 1father say: I am child,my pid:1670938,cnt: 2father say: I am child,my pid:1670938,cnt: 3father say: I am child,my pid:1670938,cnt: 4father say: I am child,my pid:1670938,cnt: 5
...
3.2 特性
- 匿名管道,只能用来进行具有血缘关系的进程进行进程间通信(常用于父子)
- 管道文件,自带同步机制
- 管道是单向通信的
- (管道)文件的生命周期,是随进程的
- 管道是面向字节流的,意味着数据以字节为单位字管道中传输
3.3 通信情况
-
写慢,读快 —读端就要阻塞(进程)
-
写快,读慢 —满了的时候,写就要阻塞等待
-
写关,继续读 —read就会读到返回值为0,表示文件结尾
-
读关闭,写继续 —写端在写入没有任何意义,OS会杀掉写端进程,发送异常信号
4.命名管道
- 让2个不相关的进程之间交换数据,可以使用FIFO文件来做这个工作,它叫做命名管道
- 命名管道是一种特殊类型的文件
4.1 创建一个命名管道
# 从命令行上创建
$ mkfifo filename#从程序里创建
int mkfifo(const char *pathname, mode_t mode);
4.2 匿名管道和命名管道的区别
- 匿名管道由pipe函数创建并打开
- 命名管道由mkfifo函数创建,open打开
- 匿名管道只能在具有血缘关系的进程里进行进程间通信
- 命名管道可以用来进行不相关的进程的进程间通信
5.System V 共享内存
- 共享内存是进程间通信中,速度最快的方式(优点):
- 映射之后,读写,直接被对方看到
- 不需要进行系统调用获取或者写入
- 共享内存没有保护机制(缺点):
- 通信双方,没有所谓的“同步机制”
- 数据不一致
5.1 共享内存函数
- shmget函数
功能:用来创建共享内存
原型:int shmget(key_t key, size_t size, int shmflg);
参数:key:这个共享内存段名字size:共享内存大小shmflg:权限标志取值IPC_CREAT:共享内存不存在,创建并返回;共享内存已存在,获取并返回取值IPC_CREAT|IPC_EXCL:共享内存不存在,创建并返回;共享内存已存在,出错返回
返回值:成功返回一个非负整数,即共享内存标识码;失败返回-1
- shmat函数
功能:将共享内存段连接到进程地址空间
原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:shmid:共享内存标识shmaddr:虚拟地址,固定地址进行挂接shmflg:它的2个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回起始的虚拟地址;失败返回-1
- shmdt函数
功能:将共享内存段与当前进程脱离
原型:int shmdt(const void *shmaddr);
参数:shmaddr:由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
- shmctl函数
功能:用于控制共享内存
原型: int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:shmid:由shmget返回的共享内存标识码cmd:要采用的行动buf:指向一个保存着共享内存模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
命令(cmd) | 说明 |
---|---|
IPC_STAT | 把shmid_ds结构中的数据设置为共享内存的当前关联值 |
IPC_SET | 在进程有足够权限的前提下,把共享内存的当前关联值设置成shmid_ds数据结构中给出的值 |
IPC_RMID | 删除共享内存段 |