进程通信
前言
Vue框架:
Vue驾校-从项目学Vue-1算法系列博客友链:
神机百炼
通信背景:
目的:
1. 数据传输:
- 含义:一个进程将其数据发送给另一进程
- 举例:下棋落一子
2. 资源共享:
- 含义:两个进程共享相同的数据
- 举例:下棋时当前的棋局
3. 事件通知:
4. 进程控制:
分类:
1. 管道通信:
- 匿名管道:适用于含有亲属关系的进程对之间
- 命名管道:适用于任意两进程之间,但常用于无亲属关系的进程
2. System V IPC:
- 消息队列
- 共享内存
- 信号量
3. Posix PC:
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
匿名管道:
原理:
- 父子进程通过将内容相同的file_struct,可以“看见同一块内存”
- 写时拷贝:
- 发生条件:父子进程对自己的内存地址空间操作时发生
- 文件操作:只有操作系统才有权力对file进行操作,进程不可直接操作
- 匿名管道:
- 管道文件:
核心函数:
pipe():
int pipefd[2];
if(pipe(pipefd) < 0){
perror(pipe error);
return -1;
}
系统调用接口:
实例演示:
- 父进程写,子进程读:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
int pipefd[2];
if(pipe(pipefd) < 0){
perror("pipe error");
return -1;
}
pid_t pid = fork();
if(pid < 0){
perror("fork error");
return -2;
}else if(pid == 0){
close(pipefd[1]);
char buffer[100];
while(read(pipefd[0], buffer, sizeof(buffer))){
printf("子进程读取到:%s\n",buffer);
}
close(pipefd[0]);
exit(0);
}else {
close(pipefd[0]);
char* msg = "pipe()匿名管道\n";
write(pipefd[1], msg, sizeof(msg));
printf("父进程写入完成\n");
close(pipefd[1]);
}
return 0;
}
linux命令:
- |:将前命令的输出,作为后命令的输入
- 举例:
who | wc -l
#who输出的是该主机上的用户
#wc -l统计输入内容条数
命名管道:
原理:
核心函数:
mkfifo():
#define FILE_NAME = "name_pipe"
#define mode 0644
if(mkfifo(FILE_NAME, mode) < 0){
perror("mkfifo error");
return -1;
}
int fd = open(FILE_NAME, O_RDWR);
if(fd < 0){
perror("open error");
return -2;
}
access():
if(access(FILE_NAME, F_OK)){
if(mkfifo(FILE_NAME, 0644) < 0){
perror("mkfifo error");
return -1;
}
}
系统调用接口:
- open():mkfile()返回值并不是直接打开命名管道的fd,所以需要我们根据FILE_NAME来手动打开文件
- close()
- write()
- read()
- 对命名管道的操作还是这四大文件调用接口,体现了linux下一切皆文件的思想
实例演示:
- 父进程读,子进程写:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#define FILE_NAME "name_pipe"
int main(){
if(access(FILE_NAME, F_OK)){
if(mkfifo(FILE_NAME, 0644) < 0){
perror("mkfifo error");
return -1;
}
}
pid_t pid = fork();
if(pid < 0){
perror("fork error");
return -2;
}else if(pid == 0){
int fd = open(FILE_NAME, O_RDONLY);
char buffer[100];
while(read(fd, buffer, sizeof(buffer))){
printf("子进程读取到:%s\n",buffer);
}
close(fd);
exit(0);
}else{
char* msg = "mkfifo命名管道\n";
int fd = open(FILE_NAME, O_WRONLY);
write(fd, msg, sizeof(msg));
printf("父进程写入完毕\n");
close(fd);
}
return 0;
}
linux命令:
-
mkfifo:
mkfifo fifo #在本目录下创建管道文件fifo
-
> >> < <<:
cmd > file #将cmd的结果输出到file中,直接覆盖file cmd >> file #将cmd的结果输出到file中,追加输出 cmd < file #将file中内容输出到cmd命令中
-
利用命名管道mkfifo实现内容写入:
共享内存:
原理:
-
基本原理:进程地址空间的共享内存部分指向内存中同一区域
-
操作步骤:
-
共享内存管理:
由于每个进程都可以创建共享内存,所以OS需要对所有共享内存进行管理
- shmid:shared memory id,每块共享内存用户层面上的独立唯一标识
- key:每块共享内存系统层面上的独立唯一标识
核心函数:
ftok():
- 作用:返回系统层面对于共享内存的唯一标识key_t
- ftok():
key_t key = ftok(FILE_NAME, PROJ_ID);
if(key < 0){
perror("ftok error");
return -1;
}
-
参数:
-
返回值:
shmget():
int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0644);
if(shmid < 0){
perror("shmget error");
return -1;
}
- 参数:
- key:系统层面对于共享内存的唯一标识
- SIZE:预计开辟共享内存的大小,由于要与磁盘中分协同,所以只能是1024的整数倍
- 打开模式:IPC_CREAT为不存在则创建,IPC_CREAT | IPC_EXCL为不存在则报错,0644为创建的共享内存权限
- 返回值:
1. 获取共享内存成功:共享内存在用户层面的唯一标识shmid
2. 获取共享内存失败:-1
shmctl:
- 作用:删除shmid所指定的共享内存
- shmctl:
shmctl(shmid, IPC_RMID, NULL);
shmat():
- 作用:将物理内存中开辟好的共享内存挂载到某个进程的进程地址空间中,供其使用
- shmat():
char* shm = shmat(shmid, NULL, 0);
- 参数:
- shmid:用户层对共享内存的唯一标识
- NULL
- 0
- 返回值:
- 共享内存的起始地址(页表映射加工过)
- shmget()时规定了共享内存的空间大小
shmdt():
- 作用:将已经挂载到某个进程的进程地址空间中的共享内存卸下
- shmdt():
shmdt(shm);
- 参数:已经挂载的共享内存地址
实例演示:
#ifndef _COMMON_H_
#define _COMMIN_H_
#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#define PATH_NAME "/home/whb/testipc"
#define PROJ_ID 0x8668 //此处可能冲突,报错就换个数字
#define SIZE 4096 //必须为1024倍数
#endif
- server.c:
#include "comm.h"
int main(){
key_t key = ftok(FILE_NAME, PROJ_ID);
if(key < 0){
perror("ftok error");
return -1;
}
int shmid = shmget(key, SIZE, IPC_CREAT);
if(shmid < 0){
perror("shmget error");
return -2;
}
char* shm = shmat(shmid, NULL, 0);
while(1){
printf("%s\n", shm);
}
shmdt(shmid);
return 0;
}
- client.c:
#include "comm.h"
int main(){
key_t key = ftok(FILE_NAME, PROJ_ID);
if(key < 0){
perror("ftok error");
return -1;
}
int shmid = shmget(key, SIZE, O_CREAT|O_EXCL|0644);
if(shmid < 0){
perror("shmger error");
return -2;
}
char* shm = shmat(shmid, NULL, 0);
int i=0;
while(1){
shm[i++] = 'A' + i;
shm[i] = 0;
sleep(1);
if(i == 27) break;
}
shmdt(shm);
return 0;
}
linux命令:
查看所有共享内存信息:
- 指令:
ipcs
###
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00005feb 0 root 666 12000 1
0x650101d0 1 whb 0 4096 0
------ Semaphore Arrays --------
key semid owner perms nsems
###
- 字段解释:
- key:ftok()的运行结果,共享内存在系统层唯一特殊标识
- shmid:shmget()的运行结果,共享内存在用户层唯一特殊标识
- owner:创建者
- perms:权限
- bytes:共享内存大小
- nattch:共享内存关联进程数
- status:共享内存当前状态
删除指定共享内存命令:
- 命令:
ipcrm -m shmid
- 共享内存由内核创建,内核维护,生命周期由OS内核决定而非具体进程
- 即使创建共享内存的进程终止,共享内存也不会释放,除非ipcrm或者关机