休闲咖啡厅网站开发目标,站内推广的方法和工具,公司注册流程2020,做网站以后的趋势文章目录 1 管道#xff08;匿名管道#xff09;1.1 管道抽象1.2 接口——pipe1.3 管道的特征1.4 管道的四种情况1.5 匿名管道用例 2 命名管道2.1 创建一个命名管道——mkfifo2.2 关闭一个管道文件——unlink2.3 管道和命名管道的补充2.4 命名管道用例 3 共享内存3.1 原理3.2… 文章目录 1 管道匿名管道1.1 管道抽象1.2 接口——pipe1.3 管道的特征1.4 管道的四种情况1.5 匿名管道用例 2 命名管道2.1 创建一个命名管道——mkfifo2.2 关闭一个管道文件——unlink2.3 管道和命名管道的补充2.4 命名管道用例 3 共享内存3.1 原理3.2 系统调用接口——shmget3.2.1 key3.2.2 ftok——返回key3.2.3 size3.2.4 shmflg3.2.5 返回值——shmid 3.3 主动释放共享内存3.3.1 使用命令——ipcrm3.3.2 系统调用函数——shmctl 3.4 将进程与共享内存挂接3.4.1 关联——shmat3.4.2 去关联——shmdt 3.5 共享内存的特点3.6 共享内存属性 —— chmctl3.5 共享内存用例 4 消息队列5 IPC在内核中的数据结构 进程间的通信标准 system V IPC 消息队列、共享内存、信号量 POSIX IPC消息队列、共享内存、信号量、互斥量、条件变量、读写锁基于文件的通信方式——管道 1 管道匿名管道 当创建子进程时进程中的files_struct同样被赋值了一份如果此时内存中打开了一份内存级文件该文件同样包含cnt、inode、file_operators、缓冲区但是该内存级文件不需要向磁盘刷新缓冲区并且在父进程的文件描述符表中那么子进程也会赋值也会同样打开这份文件此时父子进程可以通过该文件进行通信 1.1 管道抽象 管道只能进行单向通信
1.2 接口——pipe
#include unistd.hint pipe(int pipefd[2]);
//pipefd为输出型参数函数通过该参数将两个文件描述符输出出来给用户使用
//pipefd[0] : 读下标
//pipefd[1] : 写下标1.3 管道的特征
具有血缘关系的进程进行进程间通信管道只能单向通信父子进程之间是会协同的同步与互斥 —— 保护管道文件的数据安全管道是面向字节流的管道是基于文件的而文件的生命周期是随进程的当进程退出时系统自动释放文件内存 查看一些操作系统的限制例如管道的大小 ulimit -a1.4 管道的四种情况
读写端正常管道为空读端阻塞读写端正常管道被写满写端阻塞读写端正常写端关闭读端read返回0表示读到文件(pipe)结尾不会被阻塞写端正常读端关闭操作系统会通过信号(13: SIGPIPE)杀掉正在写入的进程
1.5 匿名管道用例
//匿名管道用例
#include iostream
#include cstdlib
#include unistd.h
#include cstdio
#include cstring
#include sys/wait.husing namespace std;void Writer(int write_fd) {char buffer[1023] {\0};snprintf(buffer, sizeof(buffer), %s-%d, message, pid: , getpid());write(write_fd, buffer, strlen(buffer));
}void Reader(int read_fd) {char buffer[1023] {\0};ssize_t n read(read_fd, buffer, sizeof(buffer));buffer[n] \0;cout child output: buffer endl;
}int main() {int pipefd[2];int ret pipe(pipefd);pid_t id fork();if (id 0) {close(pipefd[1]);Reader(pipefd[0]);}close(pipefd[0]);Writer(pipefd[1]);int wait_ret waitpid(id, nullptr, 0);return 0;
}2 命名管道
如果两个不同的进程打开同一个文件的时候在内核中操作系统只会打开一个文件管道文件 内存级文件只用文件缓冲区不用写入到磁盘中两个进程如何知道打开的是同一份文件使用同一个文件名
2.1 创建一个命名管道——mkfifo
与文件相同不能重复创建文件名相同的管道文件
命令 系统调用函数 2.2 关闭一个管道文件——unlink 2.3 管道和命名管道的补充 多个进程在通过管道通信时删除管道文件则无法继续通信? —— 错误 管道的生命周期随进程本质是内核中的缓冲区命名管道文件只是标识用于让多个进程找到同一块缓冲区删除后之前已经打开管道的进程依然可以通信 命名管道的本质和匿名管道的本质相同都是内核中的一块缓冲区
2.4 命名管道用例
//命名管道用例//comm.hpp
#pragma once
#include sys/types.h
#include iostream
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include signal.husing namespace std;
const string pathname ./myfifo;class Init {
public:Init() {mkfifo(pathname.c_str(), 0666);}~Init() {unlink(pathname.c_str());}
};//server.cc
#include comm.hpp
//读数据
int main() {Init init; //创建管道文件进程结束调用unlink销毁管道文件int fd open(pathname.c_str(), O_RDONLY);while (true) {char buffer[1024] {0};ssize_t n read(fd, buffer, sizeof(buffer));if (n 0) {cout write close - read close endl;break;}buffer[n] 0;cout [server read]$ buffer endl;}close(fd);return 0;
}//client.cc
#include comm.hpp
//写数据
int main() {//读端关闭发送SIGPIPE信号signal(SIGPIPE, [](int){cout read close - write close endl;// unlink(pathname.c_str());exit(0);});int fd open(pathname.c_str(), O_WRONLY);string input;while (true) {cout [client write]$ ;getline(cin, input);write(fd, input.c_str(), input.size());}close(fd);return 0;
}3 共享内存
3.1 原理
申请内存通过系统调用 通过操作系统管理所有共享内存挂接到进程地址空间返回首地址虚拟地址 3.2 系统调用接口——shmget
//创建共享内存用例
const string pathname /home/dusong;
const int proj_id 0x12345;
const int size 4096;
int main() {key_t k ftok(pathname.c_str(), proj_id);int shmid shmget(k, size, IPC_CREAT|IPC_EXCL|0X666);
}3.2.1 key
通过key标识系统中的一块共享内存对于一个已经创建好的共享内存key在哪里 key在共享内存的描述对象中
3.2.2 ftok——返回key 通过pathname和proj_id生成一个哈希值作为key
若返回值0则key获取失败
3.2.3 size
共享内存大小单位字节
共享内存的大小一般建议是40964Kb的整数倍如果size为4097操作系统会给4096*2的空间
3.2.4 shmflg
IPC_CREAT单独使用时如果申请的共享内存不存在则创建存在则获取并返回IPC_CREAT|IPC_EXCL如果申请的共享内存不存在则创建存在则出错返回确保每次申请都是一块新的共享内存IPC_EXCL不单独使用权限八进制
3.2.5 返回值——shmid
共享内存标识符shmid key与shmid的区别 key用于操作系统内标定唯一性 shmidshmget的返回值只在进程内用来表示资源的唯一性 查看所有shmid
ipcs -m 3.3 主动释放共享内存 共享内存被删除后则其它进程直接无法实现通信——错误 共享内存的删除操作并非直接删除而是拒绝后续映射只有在当前映射链接数为0时表示没有进程访问了才会真正被删除 当进程还在通过共享内存通信时通过ipcrm删除共享内存此时任然可以通信
3.3.1 使用命令——ipcrm
使用shmid关闭共享内存
ipcrm -m [shmid]删除所有进程间通信资源
ipcrm -a3.3.2 系统调用函数——shmctl 参数2cmd IPC_RMID删除共享内存IPC_STAT查看属性 参数3buf 删除共享内存时置为nullptr查看属性时作为输出型参数传入一个shmid_ds结构体描述管理共享内存的属性的结构体
3.4 将进程与共享内存挂接
3.4.1 关联——shmat 参数2shmaddr: 指定将共享内存挂接到地址空间的哪个部分一般设为nullptr
参数3shmflg: 权限设置 通常设置为0
返回值连接到虚拟地址的首地址作为shmaddr
查看一个共享内存被进程挂接的数量
ipcs -mshmat之后nattch1
1
3.4.2 去关联——shmdt
进程退出时会自动去关联 3.5 共享内存的特点
共享内存没有同步互斥之类的保护机制没有内容时不阻塞管道要阻塞共享内存是所有进程间通信中速度最快的因为拷贝少对比管道管道需要先将内容写到用户级缓冲区中再通过write写到文件缓冲区中共享内存内部的数据由用户自己维护
3.6 共享内存属性 —— chmctl const string pathname /home/dusong;
const int proj_id 0x12345;
const int size 4096;int main() {key_t k ftok(pathname.c_str(), proj_id);int shmid shmget(k, size, IPC_CREAT|IPC_EXCL|0X666);struct shmid_ds shmds;shmctl(shmid, IPC_STAT, shmds);cout shmds.shm_stgsz endl; //该共享内存的大小cout shmds.shm_prem.__key endl; //keycout shmds.shm_prem.mode endl; //权限
}3.5 共享内存用例
//comm.hpp
#pragma once
#include sys/ipc.h
#include sys/shm.h
#include sys/types.h
#include cstring
#include unistd.h
#include iostream
#include sys/stat.h
#include fcntl.h
#include signal.h
using namespace std;const string pathname ./home/for_pipe/sharedmemory;
const int proj_id 1234;
const size_t size 4096;
const string pipepath ./myfifo;class Shm {
public:Shm(){int ret mkfifo(pipepath.c_str(), 0666);if (ret 0) {cout make namedfifo fail endl;exit(1);}if (shmid 0) {cout shared_memory have created endl;exit(1);}}~Shm() {shmctl(shmid, IPC_RMID, nullptr);unlink(pipepath.c_str());}static int key;static int shmid;
};
int Shm::key ftok(pathname.c_str(), proj_id);
int Shm::shmid shmget(Shm::key, size, IPC_CREAT|IPC_EXCL|0666);//server.cc
#include comm.hpp//读端
int main() {Shm shm;int fd open(pipepath.c_str(), O_RDONLY); //通过管道保证互斥与同步char* st (char*)shmat(shm.shmid, nullptr, 0);char buffer[2] {0};while(true) {int n read(fd, buffer, sizeof(buffer));if (n 0) {cout write close - read close endl;break;}cout [sever read]$ st endl; sleep(1);}shmdt(st);
}
//client.cc
#include comm.hpp//写端
int main() {//默认信号处理为忽略signal(SIGPIPE, [](int){shmctl(Shm::shmid, IPC_RMID, nullptr);unlink(pipepath.c_str());cout read close - write close endl;exit(0);});int key ftok(pathname.c_str(), proj_id);int shmid shmget(key, size, IPC_CREAT|0666);char* st (char*)shmat(shmid, nullptr, 0); //得到共享内存在虚拟内存空间中的起始地址int fd open(pipepath.c_str(), O_WRONLY);string input ;while (true) {cout [client write]$ ;getline(cin, input);write(fd, c, 1);memcpy(st, input.c_str(), input.size()); //写数据}shmdt(st);
}4 消息队列
创建——msgget -----ftok 删除——msgctl 发送/接收数据块——msgsnd、msgrcv 查看消息队列
ipcs -q删除消息队列
ipcrm -q [msqid]5 IPC在内核中的数据结构
系统中维护一个struct ipc_prem*数组通过shmid、msqid、semid作为下标访问因为每一个shmid_ds、msqid_ds、semid_ds结构体的第一个字段均为ipc_prem所以系统可以通过将ipc_prem*强制类型转换为对应消息队列、共享内存或者信号量的结构体从而进行管理 图片中两个进程通过共享内存建立通信此时共享内存的连接数为2 ↩︎