Linux操作系统编程——进程间的通信
由于进程间空间独立,无法直接通信,所以需要IPC机制实现通信
同一主机进程间通信
1,古老的通信方式:无名管道;有名管道;信号
2,IPC对象通信system v:共享内存(效率最高);消息队列;信号量集(信号灯)
不同主机之间的通信:socket通信;网络通信
通信类型
单工:数据传输是完全单向的,通信双方严格分工,一方固定为发送端(只能发不能收),另一方固定为接收端(只能收不能发),传输方向不可改变,例如广播
半双工:数据传输允许双向进行,但同一时刻只能单向传输 ,发送和接收功能不能同时开展,需交替进行,例如对讲机
全双工:数据传输可同时双向进行,发送端和接收端能独立收发数据,无需等待切换例如打电话
古老方式
管道
无名管道:只能用于同一主机具有亲缘关系的进程通信(例如父子进程之间)
有名管道:可以用于同一主机任意进程间通信
无名管道
本质:内核空间中的一段缓冲区,遵循先进先出特性
默认大小:64K
特性:
1,写堵塞:读端和写端都存在时,向管道中写数据时,当管道满时,发生写阻塞
2,读阻塞:读端和写端都存在时,向管道中读数据时,若管道为空,发生读阻塞
3,读返回0:当写端关闭,从管道中读数据,若有数据则读到数据,若没有数据则read返回0,不再阻塞
4,管道破裂:读端关闭,向管道中写入数据。这是一种异常,操作系统会强制让进程结束
操作流程:
1)创建无名管道:pipe()
2)写管道:write()
3)读管道:read()
4)关闭管道:close()
不必刻意释放,因为属于内核区域,关闭会自动释放
pipe


参数:
pipefd[0]为读端描述符
pipefd[1]为写端描述符
二者可以作为write()和read()的实参
返回值:成功则0失败-1
管道文件应该在fork()之前创建。读写端不能交换


memset()

功能:将内存区域填充成指定数据
参数:
s:要填充的空间首地址
c:要填充的字符
n:要填充的字节数
返回值:成功则为s的首地址,失败则为NULL

输出4个A,需要注意的是,第三个参数不是sizeof(buff)是因为puts会一直读到\0,如果buff[4]也为A的话,puts会继续往后读,造成数组越界访问
一般情况下,不会让一个进程既可写又可读,保证管道的方向性,如果要两个进程互相通信,需要构建两个管道


有名管道
本质:内核空间的一段缓冲区,这段缓冲区和一个管道文件相关联
操作流程:
1,创建管道文件:mkififo()
2,打开管道文件:open()
3,读或写管道文件:write()
4,关闭管道文件:close()
5,删除管道文件:remove()
mkfifo()

功能:创建管道文件
参数:管道文件的名称;管道文件的读写执行权限(注意,是读写执行权限,并非打开方式)
返回值:成功为0失败-1
open会有阻塞的效果,当以只读的方式打开时,会阻塞直到有进程以写的方式打开,对于只写方式同理

该例子只能“运行”一次,因为再次运行写端时候会发生如下情况

这是由于有名管道已经创建,而在写端的代码中再次创建同名管道,属于正常但是错误现象,可以将创建管道的代码单独拿出来运行
有名管道实现全双工
进程

两端的代码几乎一模一样,无非是所用的管道不同,采用的是父子进程,父进程为读端,子进程为写端
运行时,在左侧下线时候打出了cd,这串字符串会被存到缓存区,待到程序运行结束时再提出来,这就是为什么最后执行了cd命令,而且自己下线时不代表对面下线,所以此时可以接收对方的信息
值得说明一下的是wait和waitpid
先说wait,放在父进程末尾,是在父进程所对应的写端结束时,该父进程也不必作为读端,直接用来阻塞回收子进程即可。
再说waitpid,如果waitpid放在while之上,那么只会尝试回收这一次,如果此时子进程未结束,父进程就继续往下执行,此时如果子进程结束,那么父进程则不会进行回收。所以waitpid放在循环内,目的是为了随时可以回收自己的子进程,且为非阻塞回收
如果没有wait只有waitpid,那么写端退出时,所对应的读端父进程则无法回收其子进程。如果没有waitpid只有wait,那么写端子进程退出时,其父进程尚未退出,即未运行到wait,则会导致该写端子进程成为孤儿进程
总的来说,wait是为了能在父进程被动结束时还留有回收子进程的能力,waitpid是为了子进程主动结束时得以被回收
线程


信号
实现进程间通知机制,实现进程间的异步通信(接收方不知道什么时候发送方会发送数据),是一种软中断
分类
系统支持的信号:
1,kill -l
2)SIGINT(crtl + c)让一个进程被打断
3)SIGQUIT(ctrl + \)让一个进程结束
9)SIGKILL(管理员信号):强制让一个进程结束
11)SIGSEGV:让一个进程结束(段错误)
13)SIGIPIPE:让一个进程结束(管道破裂)
14)SIGALRM:让一个进程结束(定时时间到达)
17)SIGCHLD:子进程结束时发送给父进程
18)SIGCONT:让停止态的进程继续运行
19)SIGSTOP(管理员信号):让运行态的进程进入停止态(暂停)强制停止
20)SIGTSTP(ctrl + z):让后台进程进入暂停态,时来自终端的停止信号
>34) 用户自定义
2,信号处理流程

在接收到信号时,会优先执行完信号所需处理的事件,再接着执行本来执行的事件
3,信号处理方式
1)缺省:按照默认处理方式
2)忽略:不处理
3)捕获:以自定义方式处理
值得注意的是,管理员信号无法被忽略和捕获,即9和19只能按照默认方式处理
signal

功能:设置信号的处理方式(注册一个信号)
参数:
signum:要处理的信号的编号
handle:
SIG_IGN:以忽略的方式处理该信号,即不处理
SIG_DFL:以缺省的方式处理,即系统默认方式
函数的地址:以捕获方式处理,即自定义,此函数需要一个整型的形参,而signum注册的信号编号会作为实参上传
返回值:失败则为NULL;自定义方式时,返回函数的地址
注意:
1,若信号不被注册,则按默认方式处理
2,信号只需注册一次
3,每次信号的到来,都会触发一次信号处理函数
4,信号尽可能早注册


如图,signal按照handler的方式运行,所以此时SIGINT信号并不能打断该进程,即按照系统默认的方式运行
发送信号
1,kill命令
2,kill()

功能:给指定的进程发送一个信号
参数:接收信号的进程的pid;信号的编号
返回值:成功则0失败-1


3,子进程结束时,会发送SIGCHLD(17)信号给父进程
子进程空间异步回收:通过子进程发送的SIGCGLD信号实现


4,raise():给自己所在的进程发送信号



5,alarm()
功能:设置一个闹钟,闹钟时间到达时,向进程发送一个SIGALRM(14)的信号
参数:秒数
返回值:成功则为上次设定剩余的时间,失败则为0
注:如果定了两个,则第一个会失去效果


上面的是没有alarm(5)的
6,pause():让一个进程进入到睡眠状态
注:pause可以被一个可以被捕获的信号唤醒
IPC
可以通过ipcs -a查看内核终端IPC对象,-s为删除信号量集,-m+id为删除共享内存
共享内存
使用内核空间中的内存区域共享数据,采用内存映射技术,减少的数据的反复拷贝,提高了通信效率,本质是通过MMU(内存管理单元)的管理将不同的内存空间映射到同一物理内存区域中,最大为32M

操作流程:
1)创建一个IPC key:key_t ftok(const char *pathname, int proj_id)
2)创建共享内存:shmget
3)建立共享内存段和用户空间的内存映射:shmat
4)向共享内存写入数据:通过用户空间首地址
5)解除映射关系:shmdt
6)删除共享内存:shmctl
ftok

功能:创建一个IPC key
参数:
pathname:路径,随便写,只需在两个进程之间创建key时传递的路径相同
proj_id:工程ID,与其他key不冲突,一般来说会用特殊的字符表示,例如'!'
返回值:成功则为IPC key,失败则为-1
注意:两个进程在创建KEY时,必须使用相同的参数
shmget


功能:创建一个共享内存
参数:
key:IPC key
size:共享内存大小,会被扩展成PAGE_SIZE(4096byte)的整数倍
shmflg:IPC_CREAT|0664(创建并给予权限)
大小不超过1G,因为内核大小只有1G,而且一般情况下共享内存最大500M
返回值:成功则为共享内存的ID,失败则为-1
shmat

功能:建立共享内存映射
参数:
shmid:共享内存的id
shmaddr:映射的用户空间首地址,如果是NULL则是让操作系统分配
shmflg:
SHM_RDONLY:只读
!SHM_RDONLY:可读可写
返回值:成功则为映射的用户空间首地址,失败则为(void *)-1
shmdt

功能:解除内存映射关系
参数:要解除的用户空间首地址
返回值:成功为0失败-1
shmctl

功能:操作共享内存
参数:
shmid:要操作的共享内存id
cmd:要执行的操作指令
IPC_RMID:删除操作
buf:设置的参数(删除的时候不需要)
返回值:成功为0失败-1
共享内存实现单工


其中那个“第一服务器”只是个名字,在本目录下必须有这么个文件才可以,不然程序会报错
信号量集
实现进线程间同步
消息队列
和管道类似,有同步的效果,相对于管道而已增加了数据的等级,可以优先处理优先级较高的数据
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)