| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 系统运维 -> 系统章节-----进程间通信 -> 正文阅读 |
|
[系统运维]系统章节-----进程间通信 |
目录 (6)阻塞:当调用pipe创建出来的读写两端的文件描述符的属性,默认都是阻塞属性 3.2共享内存是覆盖写的方式,读的时候是访问地址,没有将数据读走(注意:管道是字节流(直接将数据从管道中读走)) 1.为什么需要进程间通信??每一个进程的数据都是存储在物理内存之中的,进程通过各自的进程虚拟地址空间进行访问,访问的时候,通过各自的页表的映射关系,访问到物理内存。从进程的角度看,每个进程都认为自己有独立的4G的空间,至于物理内存当中如何存储,页表如何映射,进程是不清楚的。这也造成了进程的独立性: 好处:让每个进程在运行的时候都是独立运行的,数据不会窜。 坏处:如果两个进程之间需要数据交换,那么由于进程独立性,就没有那么方便了 所以:进程间通信本质上是进程和进程之间交换数据的手段。 2.常见的进程间通信方式:管道/共享内存/消息队列/信号量/信号/网络,其中网络是最大的,使用最广泛的进程间通信方式。 3.管道:匿名管道(1)管道符号ps aux | grep xxx (2)管道的本质管道在内核当中是一块缓冲区,供进程进行读写,交换数据。 ?(3)管道的接口》1理解参数的含义: int pipe(int pipefd[2]);? //创建匿名管道,本质上就是在内核创建出来一个缓冲区 参数:参数为出参,也就是pipe函数进行填充的,调用者进行使用 pipefd是数组,有两个元素 pipefd[0]:管道的读端 pipefd[1]:管道的写端 返回值: 0:创建成功 -1:创建失败 代码验证: ?通过创建管道并且保持管道存在的时候,我们可以看到上面这种情况。 ??(4)从PCB的角度理解管道?那么要让不同的进程进行通过匿名管道进行交换数据(进程间通信),进程应该具备什么样的条件呢? 不同的进程,要用同一个匿名管道进行通信,则进程需要拥有该管道两端的文件描述符。 代码实现父子进程的进程间通信: 如上图代码所示:我们先创建管道,再创建子进程,那么子进程就会拷贝父进程的PCB(管道是内核上的,所以不会被拷贝)但是父进程会拷贝管道的读写两端。 如下图所示: ?(5)管道的特性
pipe_size:通过ulimit -a 可以查得管道的大小为512*8=4096字节 ?原子性:一个操作要么不间断的全部被执行,要么一个也没有执行:非黑即白 管道这里的理解:即读/写操作在同一时刻只有一个进程在操作,保证进程在操作的时候,读写操作不会被其它进程打扰阻塞:读写两端的文件描述符初始的属性为阻塞属性。 (6)阻塞:当调用pipe创建出来的读写两端的文件描述符的属性,默认都是阻塞属性当write一直调用写,读不去读,则写满之后管道会阻塞 当read一直进行读,当管道内部被读完之后,则read会阻塞 (7)设置非阻塞特性int fcntl(int fd,int cmd,.../*arg*/); 参数:fd:待要操作的文件描述符 ????????? cmd:告知fcntl函数要做什么操作 ??????????? ????? F_GETFL:获取文件描述符的属性信息 ????????????????? F_SETFL:设置文件描述符的属性信息,设置的属性放到可变参数列表当中 返回值: ??????? F_GETFL:返回文件描述符的属性信息 ??????? F_SETFL:0:设置成功 ?????????????????????????? -1:设置失败 设置非阻塞属性的时候,宏的名字为O_NONBLOCK eg:第一步:先获取文件描述符原来的属性 ?????? int flag=fcntl(fd[0],F_GETFL); ??????? 第二步:设置文件描述符新的属性的时候,不要忘记将原来的属性也带上 ??????? 新的属性 = 老的属性 | 增加的属性 ??????? fcntl(fd[0],F_SETFL,flag | O_NONBLOCK); (8)非阻塞属性
读设置为非阻塞代码验证: 写设置为非阻塞代码验证: ?扩展知识:系统接口当中,文件方式打开的宏,在内核当中使用的方式是位图 eg:O_RDONLY,O_CREAT,O_NONBLACK ?O_RDONLY,O_WRONLY,O_RDWR,O_CREAT,O_TRUNC,O_APPAND,O_NONBLOCK本质上操作系统内核都是采用位图的方式继续使用。 命名管道:1.创建命名管道1.1mkfifo命令 1.2函数创建 mdfifo函数 int mkfifo(const char *pathname,mode_t mode); 参数:pathname:要创建的命名管道的路径以及文件名 ?????????? mode_t:命名管道的权限,八进制数组(0664) 返回值:成功:0 ?????????????? 失败:1 1.3特性 支持不同进程进行进程间通信,不依赖亲缘性了。 1.4代码验证 先创建命名管道: 命名管道的使用:?共享内存:1.共享内存的原理
2.共享内存的接口2.1创建或者获取共享内存的接口int shmget(key_t key, size_t size,int shmflg); 参数:key:共享内存标识符 ?????????? size:共享内存的大小 ?????????? shmflg:获取/创建共享内存时,传递的属性信息 ??????????????? IPC_CREAT:如果共享内存不存在则创建 ??????????????? IPC_EXCL | IPC_CREAT:如果获取的共享内存存在,则函数报错 ??????????????????????????????????????????????????????????? 如果获取的共享内存不存在,则创建 ????????????????本质上:该组合是要获取重新创建的共享内存 ??????????????? shmflag按位或上权限,权限是8进制数字 返回值:成功:返回共享内存的操作句柄,失败:-1 2.2将共享内存的附加到进程虚拟地址空间void *shmat(int shmid, const void *shmaddr,int shmflag); 参数:shmid:共享内存的操作句柄 ??? ?????? shmaddr:将共享内存附加到共享区当中的哪一个地址上。一般让操作系统自己分配,传递NULL; ??????? shmflag:以什么权限将共享内存附加到进程中 ??????????????? SHM_RDONLY:只读 ??????????????? 0:可读可写 返回值:成功:返回附加的虚拟地址 ?????????????? 失败:NULL; 2.3分离int shmdt(const void *shmadrr); 参数:shmaddr:shmat的返回值 返回值:成功:0 ?????????????? 失败:-1 2.4操作共享内存接口int shmctl(int shmid, int cmd,struct ds *buf); 参数:shmid:共享内存的操作句柄 ? ? ? ? ? ?cmd:告诉shmctl函数需要完成什么功能 ? ? ? ? ? ? ? ? IPC_SET:设置共享内存属性信息 ? ? ? ? ? ? ? ? IPC_START:获取共享内存的属性信息 ? ? ? ? ? ? ? ? IPC_RMID:删除共享内存,第三个参数为NULL ? ? ? ? ? ?buf:共享内存的数据结构buf 3.共享内存的特性:3.1生命周期跟随操作系统3.2共享内存是覆盖写的方式,读的时候是访问地址,没有将数据读走(注意:管道是字节流(直接将数据从管道中读走))3.3共享内存的删除特性ipcs命令 & ipcrm命令
此时是连接的进程不为0,则删除共享内存的时候就有如下现象:? ?? ?当我们把连接的进程结束的时候它的共享内存就会直接被释放: 消息队列1.消息队列的原理
这里画一幅图来理解:注意:只要满足先进先出特性的数据结构都可以称之为队列 ?2.消息队列的接口创建消息队列int msgget(key_t key,? int msgflg); 参数:key:消息队列的标识符 ? ? ? ? ? ?msgflg:创建的标志,例如:IPC_CREAT ? ? ? ? ? ? ? ? IPC_CREAT:如果不存在,则创建 ? ? ? ? ? ? ? ? 按位或(|)权限(8进制的数字就可以了) 返回值:成功:返回队列ID ? ? ? ? ? ? ? ?失败:返回-1,并设置erron 发送消息int msgsnd(int msqid,const void *msgp, size_t msgsz, int msgflg); 参数:msgid:消息队列的ID ? ? ? ? ? ?msgp:指向msgbuf的指针,用来指定发送的消息 ? ? ? ? ? ?操作系统为该函数发送的消息定义了发送格式,只是定义了一部分,另外一部分还需要程序员自己定义。 ? ? ? ? msgsz:要发送消息的长度 ? ? ? ? msgflg:创建标记,如果指定IPC_NOWAIT,失败会立刻返回 ? ? ? ? ? ? ? ? ? ? ? ?0:阻塞发送 ? ? ? ? ? ? ? ? ? ? ? ? IPC_NOWAIT:非阻塞放松 返回值: ? ? ? ? 成功返回 0 ? ? ? ? 失败返回-1,并设置erron 接收消息ssize_t? msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg); 参数:msgid:消息队列的ID ? ? ? ? ? ?msgp:指向msgbuf的指针,用来接收消息 ? ? ? ? ? ?msgsz:要接收消息的长度 ? ? ? ? ? ? ? ? 注意:参数msgsz指定由msgp参数指向的结构的成员mtext的最大大小(以字节为单位),msgtyp也有三种方式 ? ? ? ? ? ?msgtyp:接受消息的方式 ? ? ? ? ? ? ? ? ? ? ? ? 1.msgtyp = 0:读取队列中的第一条消息 ? ? ? ? ? ? ? ? ? ? ? ? 2.msgtyp > 0:读取队列中类型为msgtyp的第一条消息(从消息的发射我们就可以看出,队列中消息的类型是用一个大于0的整数表示的)。如果在msgflg中指定了MSG_EXCEPT,将读取类型不等于msgtyp的队列中的第一条消息。 ? ? ? ? ? ? ? ? ? ? ? ? 3.msgtyp < 0:读取消息队列中最小类型小于或等于msgtyp绝对值的第一条消息 ? ? ? ? ? ?msgflg:创建标记,如果指定IPC_NOWAIT,获取失败会立刻返回 返回值:成功返回实际读取消息的字节数 ? ? ? ? ? ? ? ? 失败返回-1,并设置erron 操做消息队列的接口int msgctl(int msgqid,int cmd,struct msqid_ds *buf); 参数:msqid:消息队列ID ? ? ? ? ? ?cmd:控制命令, ? ? ? ? ? ? ? ? ? ? ? 例如:IPC_RMID,删除命令 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IPC_STAT,获取状态 ? ? ? ? ? ?buf:存储消息队列的相关消息的buf 返回值:成功根据不同的cmd有不同的返回值 ? ? ? ? ? ? ? ? 失败返回-1,并设置erron 代码测试:我们循环发送了两轮类型从1到10的消息 |
|
|
上一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/15 23:44:24- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |