进程具有独立性,因此进程间想要通信,成本会非常高,但有时候又需要多个进程协同处理一件事,所以进程间通信是必不可少的
从一个进程连接到另一个进程的一个数据流称为一个“管道”
显示器是缺乏管道控制的,所以父子进程在向显示写入的时候是无序的,而管道是自带访问控制机制的,如果管道内部没有数据,reader就必须阻塞等待;如果管道内部被写满了,writer就必须阻塞等待
代码:
#include
#include
#include
#include
#include
using namespace std;
int main()
{
//1.创建管道
int pipefd[2] = {0};
if(pipe(pipefd) != 0)//创建一个管道成功返回0
{
cerr<<"pipe error\n"< 0)
{
//读取成功
buffer[s] = '\0';
cout<<"子进程收到消息,内容是:"< 0)
{
cout<<"等待子进程成功"<
进程池:通过父进程控制一批子进程
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef void (*functor)();//函数指针
vector functors;//方法集合
unordered_map info; //uint32_t = unsigned int
void f1()
{
cout<<"这是一个处理日志的任务,执行进程ID ["<
typedef pair elem;
int processNum = 5;
void work(int blockFd)
{
cout<<"进程["< &processFds)
{
srand((long long)time(nullptr));
while(true)
{
sleep(1);
//随机选择一个子进程
//较为均匀的将任务给所有的子进程 ---- 负载均衡
uint32_t pick = rand() % processFds.size();
//选择一个任务
uint32_t task = rand() % functors.size();
//把任务给一个指定的进程
write(processFds[pick].second,&task,sizeof(task));
//打印对应的提示信息
cout<<"父进程指派任务->"< assignMap;
//创建processNum个进程
for(int i = 0;i < processNum; i++)
{
//定义保存管道fd的对象
int pipefd[2] = {0};
//创建管道
pipe(pipefd);
//创建子进程
pid_t id = fork();
if(id == 0)
{
//子进程读取,r->pipefd[0]
close(pipefd[1]);
//子进程执行
work(pipefd[0]);
close(pipefd[0]);
exit(0);
}
//父进程做的事情
close(pipefd[0]);
elem e(id,pipefd[1]);
assignMap.push_back(e);
}
cout<<"create all processs success!"< 0)
{
cout << "wait for: pid="<
管道的特征
1、管道只能用来进行具有血缘关系的进程之间,进行进程间通信,常用于父子通信
2、管道只能单向通信
3、管道自带同步机制(pipe满,writer等;pipe空,reader等)
4、管道是面向字节流的 ---- 先写的字符一定是先被读取的,没有格式边界,需要用户规定读多少字节
5、管道的生命周期 ---- 随进程
继承了匿名管道的所有特性,并且允许两个没有血缘关系的进程进行通信
匿名管道是通过子进程继承父进程实现的,而命名管道是通过不同进程访问同一文件(fifo文件)实现的
//comm
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IPC_PATH "./.fifo"
//读取 serverFifo
#include "comm.h"
using namespace std;
#define NUM 1024
int main()
{
umask(0);
if(mkfifo(IPC_PATH,0600) != 0)//成功返回0
{
cerr<<"mkfifo error"< 0)
{
buffer[s] = '\0';
cout<<"客户端->服务器#"<
创建共享内存 shmget
key可以自己设置人任意值,只要保证它的唯一性就好,但是一般都是用ftok()来进行设置
删除共享内存 shmctl
shmctl(shmid, IPC_RMID,nullptr)
使用共享内存 shmat 成功返回共享内存的地址,失败返回-1
取消关联 shmdt
ipc 命令:
ipcs -m : 显示当前用户创建的共享内存
ipcrm -m + shmid: 删除共享内存
共享内存属于双方的用户空间(堆、栈),可以直接访问共享内存,不用使用系统接口,但是没有任何访问控制,不安全
用管道来对共享内存增加访问控制
//comm
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include"Log.hpp"
#include
#include
#include
#include
#include
#define PATH_NAME "/home/ldx/code/2022_11_10"
#define PROJ_ID 0x14
#define MEM_SIZE 4096
#define FIFO_FILE ".fifo"
key_t CreateKey()
{
key_t key = ftok(PATH_NAME,PROJ_ID);
if(key < 0)
{
std::cerr << "ftok:" << strerror(errno) << std::endl;
exit(1);
}
return key;
}
void CreateFifo()
{
umask(0);
if(mkfifo(FIFO_FILE,0666) < 0) //创造管道文件 “.fifo”
{
Log() << strerror(errno) << '\n';
exit(2);
}
}
#define READER O_RDONLY
#define WRITER O_WRONLY
int Open(const std::string& filename,int flags)
{
return open(filename.c_str(),flags);
}
int Wait(int fd)
{
uint32_t values = 0;
ssize_t s = read(fd, &values, sizeof(values));
return s;
}
int Signal(int fd)
{
uint32_t cmd = 1;
write(fd, &cmd, sizeof(cmd));
}
int Close(int fd, const std::string filename)
{
close(fd);
unlink(filename.c_str());
}
//Log
#pragma once
#include
#include
std::ostream& Log()
{
std::cout<<"for debuge |" <<"timestamp"<<(uint64_t)time(nullptr)<<"|";
return std::cout;
}
//Cli
#include "Comm.hpp"
#include "Log.hpp"
#include
#include
using namespace std;
// 充当使用共享内存的角色
int main()
{
int fd = Open(FIFO_FILE, WRITER);
// 创建相同的key值
key_t key = CreateKey();
Log() << "key: " << key << "\n";
// 获取共享内存
int shmid = shmget(key, MEM_SIZE, IPC_CREAT);
if (shmid < 0)
{
Log() << "shmget: " << strerror(errno) << "\n";
return 2;
}
// 挂接
char *str = (char*)shmat(shmid, nullptr, 0);
while(true)
{
printf("Please Enter# ");
fflush(stdout);
ssize_t s = read(0, str, MEM_SIZE);
if(s > 0)
{
str[s] = '\0';
}
Signal(fd);//标记写完了
}
// 去关联
shmdt(str);
return 0;
}
//Ser
#include "Comm.hpp"
#include "Log.hpp"
#include
using namespace std;
// 我想创建全新的共享内存
const int flags = IPC_CREAT | IPC_EXCL;
// 充当使用共享内存的角色
int main()
{
CreateFifo();
int fd = Open(FIFO_FILE, READER);
assert(fd >= 0);
key_t key = CreateKey();
Log() << "key: " << key << "\n";
Log() << "create share memory begin\n";
int shmid = shmget(key, MEM_SIZE, flags | 0666);
if (shmid < 0)
{
Log() << "shmget: " << strerror(errno) << "\n";
return 2;
}
Log() << "create shm success, shmid: " << shmid << "\n";
// 1. 将共享内存和自己的进程产生关联attach
char *str = (char *)shmat(shmid, nullptr, 0);
Log() << "attach shm : " << shmid << " success\n";
// 用它
while(true)
{
// 让读端进行等待
if(Wait(fd) <= 0) break;
printf("%s\n", str);
sleep(1);
}
// 2. 去关联
shmdt(str);
Log() << "detach shm : " << shmid << " success\n";
// sleep(5);
// 删它
shmctl(shmid, IPC_RMID, nullptr);
Log() << "delete shm : " << shmid << " success\n";
Close(fd, FIFO_FILE);
// sleep(5);
return 0;
}
可以被多个程序看到的资源叫临界资源,访问临界资源的代码叫临界区
信号量就是一个计数器,这个计算器对应的操作是原子的
信号量对应的操作:
sem:-- ,申请资源:P
sem:++,释放资源,V