Linux:进程间通信与信号
Posted 银背欧尼酱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux:进程间通信与信号相关的知识,希望对你有一定的参考价值。
文章目录
一,共享内存
每一个进程都想要访问物理内存,都是通过访问进程虚拟地址空间当中的虚拟地址来访问,访问的时候,通过各自的页表结构。造成了每一个进程和每一个进程的数据独立,虽然有进程独立性的存在,进程运行的时候,不会相互干扰。但是,就造成了进程与进程之间相互协作的难点。
学习进程间通信的手段,本质上是为了进程与进程交换数据的时候使用的
常见的进程间通讯的方式:
1.管道
2.共享内存
3.消息队列&信号量
4.信号最大的进程间通信方式:网络
二,管道
管道的分类:匿名管道和命令管道
2.1 匿名管道
2.1.1 管道符号
ps aux | grep xxx
ps : 本质上都是可执行程序
grep : 可执行程序
2.1.2 管道的本质
管道在内核当中是一块缓冲区,供不同的进程进行读写的缓冲区
2.1.3 管道的接口
int pipe(int pipefd[2]);
pipefd:数组,输出型参数
pipefd[0],pipefd[1]是pipe函数进行填充的
参数当中保存的值是文件描述符,两个文件描述符分别对应管道的读写两端。
pipefd[0]:管道的读端
pipefd[1]:管道的写端
返回值:
0:创建匿名管道成功
-1:创建失败
在进行父子进程通信的时候,一定要父进程先创建管道,再创建子进程,此时子进程的文件描述符表当中才会有匿名管道的读写两端的文件描述符。
2.1.4 管道特性
-
1.管道是半双工通信的,数据流只能从写端流向读端。
-
2.匿名管道在内核创建出来的缓冲区是没有标识符的,导致了其他进程没有办法直接找到这个缓冲区。但是创建的进程可以通过读写两端的文件描述符进行操作。
-
3.匿名管道只支持具有亲缘性关系的进程进行进程间通信
-
4.当文件描述符保持基础属性(阻塞),调用read读空管道的时候,则read函数就会阻塞
-
5.管道的大小为64k
-
6.当文件描述符保持基础属性(阻塞),一直调用write将管道写满之后,则write函数就会阻塞
-
7.管道的生命周期是跟随进程的
-
8.管道提供字节流服务
字节流:描述符的前后两个数据之间是没有明显边界的
- 9.从fd[1]当中读取内容的时间,是直接将数据读走了,并不是拷贝
- 10.管道的大小:64k
- 11.当对管道进行读写的时候,如果读写的字节大小没有超过pipe_size(4096字节),则管道保证读写的原子性(原子性:非黑即白,不存在中间状态,要么完成了,要么没有开始)、
2.1.5 匿名管道的非阻塞特性
读设置称为非阻塞
写不关闭:一直读,读端调用read函数之后,返回值为 -1 ,errno置为EAGAIN
写关闭:一直读,读端read函数返回 0,表示什么都没有读到
写设置称为非阻塞:
读不关闭:一直写,当把管道写满之后,则再调用write,就会返回-1
读关闭:一直写,写端调用write进行写的时候,就会发生崩溃。本质上是写端的进程收到了SIGPIPE信号,导致了写端的进程崩溃。
2.1.6管道创建流程
1.创建管道
2.设置文件描述符属性
3.创建子进程
4.父子进程验证逻辑
2.2 命名管道
命名管道与匿名管道的区别:是有标识符的管道,其他进程可以通过管道标识找到该管道。
创建:
命令创建:
mkfifo
函数创建
mkfifo函数
int mkfifo(const char *pathname,mode_t mode);
pathname:要创建的命名管道文件的路径
mode_t : 命名管道文件的权限,八进制数字(0664)
特性:
支持不同进程进行进程间通信的
三,共享内存
3.1 共享内存的原理
- 在物理内存当中开辟一段空间
- 不同的进程通过页表将该空间映射到自己的进程虚拟地址空间当中
- 不同的进程通过操作自己进程虚拟地址空间当中的虚拟地址,来操作共享内存
3.2 共享内存接口
创建或者获取共享内存
int shmget(key_t key,size_t size,int shmflg);
key_t : 共享内存的标识符
size : 共享内存的大小
shmflg : 共享内存的属性信息
IPC_CREAT : 共享内存不存在的话,则创建共享内存
IPC_EXCL | IPC_CREAT : 如果共享内存存在,则报错。如果共享内存不存在,则创建。(一定要获得一个自己创建的共享内存)
按位或上共享内存的权限,权限也是8进制数字
返回值:
成功:则返回共享内存的操作句柄
void shmat(int shmid,const void *shmaddr,int shmflg);
shmid : 共享内存操作句柄
shmaddr : 将共享内存附加到共享区当中的哪一个地址。一般是传递NULL值,让操作系统进行分配。
shmflg : 共享内存的属性信息
SHM_RDONLY : 只读方式
0 : 可读可写
返回值:
成功:则返回附加的虚拟地址
失败:NULL
int shmctl(int shmid, int cmd,struct shmid_ds *buf);
shmid : 共享内存操作句柄
cmd : 告诉shmctl函数完成什么事情。
IPC_SET : 设置共享内存属性信息,通过第三个参数(入参)
IPC_STAT : 获取共享内存属性信息,通过第三个参数获取(出参)
IPC_RMID : 删除共享内存,第三个参数传递NULL
buf : 共享内存数据结构buf
3.3 共享内存的特性:
1.共享内存是覆盖写的方式,读的时候,是访问地址
2.共享内存的声明周期跟随操作系统内核
3.一旦共享内存被删除之后,其本质共享内存的内存空间已经被删除了
4.如果在删除的时候,共享内存附加的进程数量为0,则内核中描述共享内存的结构体也就被释放了。
5.如果在删除的时候,共享内存附加的进程数量不为0,则会将该共享内存的key,变成0x00000000。表示当前共享内存不能被其他进程所附加,共享内存的状态就被置为了destroy。描述符共享内存的结构体内部的引用计数一旦为0,则该共享内存结构体被干掉。
四,信号
4.1 进程信号的分类
1.信号的概念
2.信号的种类
3.信号的产生方式
4.信号的注册
5.信号的注销
6.信号的处理方式
7.信号的捕捉流程
8.信号的阻塞
9.其他
4.2 信号的概念
信号是进程之间事件异步通知的一种方式,属于软中断。
4.3 信号的种类
非可靠信号(非实时信号):
【1,31】:不可靠,信号有可能会丢失
可靠信号(实时(real time)信号):
【34,64】:可靠信号,信号绝对不会丢失
4.4 信号的产生方式
硬件产生
ctrl + c : 2号信号
ctrl + l : 3号信号 SIGQUIT
ctrl + z : 20号信号 SIGTSTP
软件产生
kill 命令
kill -9 命令
函数:
abort() -->6号信号
alarm函数 --> 14号信号
sigqueue队列
前提:同一个信号注册两次
非可靠信号的注册:
第一次:
将信号对应的比特位改成1 +, 在sigqueue队列中添加sigqueue节点
第二次:
只会将信号对应的比特位从1改成1 , 不添加sigqueue节点
可靠信号的注册:
第一次:
将信号对应的比特位改成1 +, 在sigqueue队列中添加sigqueue节点
第二次:
会在sigqueue队列中添加可靠信号的sigqueue节点
4.5 信号的注销
非可靠信号的注销:
将信号对应在sig位图当中的比特位置为0,并且将sigqueue节点从sigqueue队列中进行出队列操作。
可靠信号的注销:
第一步:
将可靠信号对应的sigqueue节点从sigqueue队列中进行出队列操作。
第二步:
判断sigqueue队列中是否还有当前可靠信号的sigqueue节点。
如果没有:则将sig位图当中可靠信号对应的比特位置为0
如果有:则不会将sig位图当中的可靠信号对应的比特位置为0
4.6 信号的处理方式
默认处理方式 --》SIGDFL
忽略处理 --》SIGIGN
SIGCHILD信号 --》子进程在退出的时候会给父进程发送一个SIGCHILD信号
自定义处理:想要完成更改信号的处理方式
总结
以上就是今天内容,介绍了什么是共享内存,共享内存存在的意义。什么是命名管道,什么是匿名管道,二者的区别以及信号的相关知识。
希望大家认真学习,有所收获。
以上是关于Linux:进程间通信与信号的主要内容,如果未能解决你的问题,请参考以下文章