进程间通信

进程间通信的目的

数据传输: 一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另个进程的所有陷入和异常,并能够及时知道它的状态改变。

管道通信

管道是os中最早的进程通信方式

从一个进程连接搭配另一个进程的数据流称为一个管道


匿名管道

进程通信的的本质就是让不同的进程看到同一块“内存”(特定的数据结构),这一块内存不属于任何一个进程,更应该是共享出来的。那么管道的技术就是给输入和输出进程各有一个口,管道的内容都是单向传输的,传输的就是数据,这种方式在通信的专业名字叫半双工。

那么匿名管道是怎么实现的?

1.分别以读写的方式打开同一个问题

2.fork()创建子进程

3.双方进程各自关闭自己不需要的文件描述符,比如子进程读数据就关闭写文件描述符,父进程写数据就关闭读的文件描述符

以子进程读数据就关闭写文件描述符,父进程写数据就关闭读的文件描述符举例

父进程write数据,child去read数据,他们用同一个地址空间去访问,这种方法可以用一个缓冲区去当中间地址空间。

pipe

man 2 pipe

先来查看一下管道的接口函数,直接调用一个大小为2的数组就能完成创建。

这里父进程在sleep3秒后在buff写入字符串数据,子进程使用read阻塞式等待数据传来,父进程如果不传数据子进程就一直等待。

#include<iostream>
#include<vector>
#include<unistd.h>
#include<assert.h>
#include <cstdlib>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include<string.h>
#include<string>
using namespace std;

int main(){
    //1.先创建大小为2的数组,用来关闭输入或者输出
    int pipefd[2]={0};
    int n=pipe(pipefd);
    cout<<n<<endl;
    assert(n==0);
    (void)n;
    //2.建立缓冲区buff
    char buff[64]={};
    pid_t id=fork();

    if(id==0){
        //4.子进程读取
        while(true){
            cout<<"wait father"<<endl;
    close(pipefd[1]);
    read(pipefd[0],buff,sizeof(buff));
     cout<<buff<<endl;
    cout<<"this is child:"<<buff<<endl;
    exit(1);
        }
    
    }else{  
        //5.父进程写入
    close(pipefd[0]);
    const char* str="hello word hahahaha";
    strcpy(buff,str);
    cout<<buff<<endl;
    cout<<"this is father"<<endl; 
    sleep(3); 
    write(pipefd[1],buff,strlen(buff));
    }
    pid_t waitid=waitpid(id,NULL,0);
    cout<<waitid<<endl;
    return 0;
}

进程池模拟

先拆开看最后再来看整体代码

1.先加载任务和定义一个处理任务的slots,其中slots采用pair键值对插入

其中load的执行流程是这样的,分别对desc和callback插入任务和执行任务进行

2.假设有五个进程,我们要让他随机取任务进行操作,那么每个进程都会去取随机的操作,然后将该操作通过pipe传输给子进程。

父进程和子进程的操作

调用waitCommand函数,如果没有数据,那么就一直等待。

随机选择任务进行处理

关闭父进程写的窗口和回收子进程

Task.hpp

#pragma once

#include<iostream>
#include<unordered_map>
#include<string>
#include<functional>
#include<vector>
#include <sys/types.h>
#include <unistd.h>
using namespace std;

typedef std::function<void()> func;
vector<func> callbacks;
unordered_map<int ,string> desc;

void readMySQL(){
    std::cout<<"sub process["<<getpid()<<"]执行访问数据库任务\n"<<std::endl;
}

void execuleUrl(){
    std::cout<<"sub process["<<getpid()<<"]执行url解析\n"<<std::endl;
}
void cal(){
    std::cout<<"sub process["<<getpid()<<"]执行加密任务\n"<<std::endl;
}
void save(){
    std::cout<<"sub process["<<getpid()<<"]执行数据库持久化任务\n"<<std::endl;
}


void load(){
    desc.insert({callbacks.size(),"readMySQL:读取数据库"});
    callbacks.push_back(readMySQL); 
desc.insert({callbacks.size(),"execuleUrl:进行url解析"});
    callbacks.push_back(execuleUrl);
 desc.insert({callbacks.size(),"cal:进行加密计算"});
    callbacks.push_back(cal);
desc.insert({callbacks.size(),"save进行数据的文件保存"});
    callbacks.push_back(save);
}
void showHandler()
{
    for(const auto &iter : desc )
    {
        std::cout << iter.first << "\t" << iter.second << std::endl;
    }
}

int handlerSize(){
    return callbacks.size();
}

process.cc

#include<iostream>
#include<vector>
#include<unistd.h>
#include<assert.h>
#include <cstdlib>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include"Task.hpp"

#define PROCESS_NUM 5
using namespace std;

int waitCommand(int waitFd,bool &quit){
   uint32_t command=0;
   ssize_t s=read(waitFd,&command,sizeof(command));
    if(s==0){
        quit=true;
        return -1;
    }
    assert(s==sizeof(uint32_t));
    return command;
}
void sendAndWakeup(pid_t who,int fd,uint32_t command){
    write(fd,&command,sizeof(command));
    cout<<"main process:call process"<<who<<"execute"<<desc[command]<<"through"<<fd<<endl;
}
int main(){
    load();//加载任务
    vector<pair<pid_t,int>> slots;//插入处理任务

    for(int i=0;i<PROCESS_NUM;i++){
        int pipefd[2]={0};//创建管道
        int n=pipe(pipefd);//0代表读1代表写
        assert(n==0);
        (void)n;

        pid_t id=fork();
        assert(id!=-1);

        if(id==0){
            close(pipefd[1]);
            while(true){
            bool quit=false;
            int command=waitCommand(pipefd[0],quit);
            if(quit)break;
            if(command>=0&&command<handlerSize()){
                callbacks[command]();
            }else{
                cout<<"非法commond:"<<command<<endl;
            }
            }
        exit(1);
        }
    //父亲写入关闭读端
    close(pipefd[0]);
    slots.push_back(pair<pid_t,int>(id,pipefd[1]));
    }
    showHandler();
    srand((unsigned long)time(nullptr)^getpid()^23333L);
    int n=5;
    while(true){
        int command=rand()%handlerSize();
        int choice=rand()%slots.size();
        sendAndWakeup(slots[choice].first,slots[choice].second,command);
        sleep(1);   
    }
    for(const auto &slot:slots){
        close(slot.second);
    }
    for(const auto &slot:slots){
        waitpid(slot.first,nullptr,0);
    }
}

Makefile

mypipe:mypipe.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f mypipe

匿名管道总结

1.管道是用来进行具有血缘关系的进程进性进程间通信
2.管道具有通过让进程间协同,提供了访问控制!
3.管道提供的是面向流式的通信服务 -- 面向字节流 --协议

4.管道是基于文件的,文件的生命周期是随进程的,管道的生命周期是随进程的!4

5.管道是单向通信的 ,就是半双工通信的一种特殊情况

a.写快,读慢,写满不能在写了

b.写慢,读快,管道没有数据的时候,读必须等待

c.写关,读到‘\0’位置,标识读到了文件结尾
d.读关,写继续写,写满的话OS终止写进程

命名管道

先让不同的的进程看到同一份资源,双方进程就可以通过管道文件的路径,看到同一份资源,该路径在系统中一定是唯一的!管道文件可以被打开,但是并不会将数据刷新到磁盘上。

 mkfifo

根据路径创建一个管道,mode代表权限的意思,成功就返回0,失败就返回-1

man 3 mkfifo

                1就代表管道文件

设置一个路径,能让server和client都能找到的

建立日志文件

创建客户端用来发消息

 创建服务端用来接收消息

 命名管道代码

client.cc

#include"common.hpp"

int main(){
   
int fd=open(ipcpath.c_str(),O_WRONLY);
    if(fd<0){
        perror("open");
        exit(1);
    }
    string buff;
  log("登陆服务器成功",0);
  cout<<"please cin string"<<endl;
  while(true){
    log("消息",1);
    std::getline(std::cin,buff);
    write(fd,buff.c_str(),sizeof buff);
    if(strcmp(buff.c_str(),"quit")==0){
        break;
    }
 
  }

   close(fd);
    return 0;

}

server.cc

#include"common.hpp"

int main(){
   int pipe=mkfifo(ipcpath.c_str(),0666);
assert(pipe=-1);
(void)pipe;
   log("创建管道成功",0);
   int fd=open(ipcpath.c_str(),O_RDONLY);
   if(fd<0){
    perror("open");
    exit(1);
   }
   log("打开文件成功,等待读取",0);

   while(true){
    char buff[64];
    read(fd,buff,sizeof(buff));
    cout<<buff<<endl;
    if(strcmp(buff,"quit")==0){
      break;
    }
     memset(buff,'\0',sizeof buff);
   }
   log("退出服务器",2);
   unlink(ipcpath.c_str());
   log("删除管道",3);
}

common.hpp

#pragma once
#include<iostream>
#include<cstring>
#include<cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include<cstdlib>
#include<assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <ctime>
#include"Log.hpp"

using namespace std;

 std::string ipcpath="./fifo.ipc";

Makefile

.PHONY:all
all: server client

server:Server.cc
	g++ -o $@ $^ -std=c++11
client:Client.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f server client

Log.hpp

#include"common.hpp"

#define Debug   0
#define Notice  1
#define Warning 2
#define Error   3

const std::string msg[] = {
    "Debug",
    "Notice",
    "Warning",
    "Error"
};
std::ostream &log(std::string message,int level){
    std::cout<<"|"<<(unsigned)time(nullptr)<<"|"<<msg[level]<<"|"<<message<<std::endl;
    return std::cout;
}

system V共享内存

共性内存的建立

共享内存提供者,是操作系统操作系统要不要管理共享内存?

当然要->先描述在组织->重新理解 共享内存 = 共享内存块 +对应的共享内存的内核数据结构

创建共享内存接口shmget

共享内存的用户层标识符,类似曾经的fd

shmflg

IPC_CREAT and IPC_EXCL

如果创建共享内存,如果底层已经存在,获取之,并且返回,如果不存在,创建之,并返回

IPC_CREAT and IPC_EXCL

如果底层不存在,创建之,并返回如果底层存在,出错返回。那么就意外着返回成功一定是全新的shm。

key

通过key,数据是几,不重要只要能够在系统唯一即可 -> server && client ->使用同一个key ->只要key值相同,就是看到了同一个共享内存!

ftok函数

使用同样的算法规则形成唯一值!
key t ftok(Const char *pathname, int pro id);

那么共享内存的好处就是不用经过系统调用,直接可以访问!

双方进程如果要通信,直接进行内存级的读和写即可!

ftok

查看根据路径和值生成一个共享key,这个key用于双方通信

shmget

用于创建共享内存,如果双方的key一样的话,那么就可以通信

shmat

用法和malloc差不多,在堆上开辟出一块空间。

shmdt

断开共享内存连接,成功返回0,失败返回-1

shmctl

删除共享内存,成功返回0,失败返回-1

ipcs和ipcrm

ipcs可以查看有几个共享内存,而ipcrm可以用来删除共享内存

共享内存实现

memserver.cc

#include"common.hpp"


int main(){

    key_t k=ftok(PATH_NAME,PROJ_ID);
    if(k<0){
        log("create key failed",Error)<<"server key"<<k<<endl;
        exit(1);
    }
     log("create key success",Debug)<<"server key"<<k<<endl;

    int shmid=shmget(k,SHM_SIZE,IPC_CREAT|IPC_EXCL|0666);
    if(shmid<0){
    log("create shm failed",Error)<<"server shmid"<<shmid<<endl;
        exit(2);
    }   
    log("create shm success",Debug)<<"server shmid"<<shmid<<endl;
    
    char * shmaddress=(char *)shmat(shmid,nullptr,0);
    if(shmaddress==nullptr){
        log("create shmaddress failed",Error)<<"server shmid"<<shmid<<endl;
        exit(3);
    }
     log("create shmaddress success",Debug)<<"server shmid"<<shmid<<endl;

    // int fd=OpenFIFO(PATH_NAME,O_RDONLY);
    int fd=OpenFIFO(FIFO_NAME,O_RDONLY);
    while(true){
        Wait(fd);
       printf("%s\n",shmaddress);
       if(strcmp(shmaddress,"quit")==0)break;
      
    }

    int n=shmdt(shmaddress);
     if(n<0){
        log("deatch shm failed",Error)<<"server shmid"<<n<<endl;
        exit(4);
    }
    log("deatch shm success",Debug)<<"server shmid"<<n<<endl;


    int ctl=shmctl(shmid,IPC_RMID,nullptr);
    if(ctl<0){
        log("delete shm failed",Error)<<"server shmid"<<ctl<<endl;
    }
    log("delete shm success",Debug)<<"server shmid"<<ctl<<endl;
    return 0;
}

shmclient.cc

#include"common.hpp"

int main(){
    key_t k=ftok(PATH_NAME,PROJ_ID);
    if(k<0){
        log("create key failed",Error)<<"client key"<<k<<endl;
        exit(1);
    }
    log("create key success",Debug)<<"client key"<<k<<endl;
    int shmid=shmget(k,SHM_SIZE,0);
    if(shmid<0){
        log("create shm failed",Error)<<"client shmid"<<shmid<<endl;
        exit(2);
    }
     log("create shm success",Debug)<<"client shmid"<<shmid<<endl;

    sleep(2);

    char* shmaddress=(char*)shmat(shmid,nullptr,0);
    if(shmaddress==nullptr){
       log("create shmaddress failed",Error)<<"client shmid"<<shmid<<endl;
        exit(3);
    }
    log("create shmaddress success",Debug)<<"client shmid"<<shmid<<endl;
        int fd=OpenFIFO(FIFO_NAME,O_WRONLY);
        while(true){
            ssize_t s=read(0,shmaddress,SHM_SIZE-1);
            if(s>0){
                shmaddress[s-1]=0;
                Signal(fd);
                if(strcmp(shmaddress,"quit")==0)break;
            }   
            
        }
        CloseFifo(fd);
    sleep(2);
    int n=shmdt(shmaddress);
    if(n<0){
        log("deatch shm failed",Error)<<"client shmid"<<n<<endl;
        exit(4);
    }
    log("deatch shm success",Debug)<<"client shmid"<<n<<endl;
    sleep(2);
   
    return 0;
}

common.hpp

#pragma once
#include<iostream>
#include<cstring>
#include<cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include<cstdlib>
#include<assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <ctime>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include"Log.hpp"

using namespace std;

#define PATH_NAME "/home/user_001/lesson25/shrmem"//路径
#define PROJ_ID 0x66//随机数
#define SHM_SIZE 4096//共享内存最好是page4096的整数倍
#define FIFO_NAME "./fifo"

class Init{

public:
    Init(){
        umask(0);
        int n=mkfifo(FIFO_NAME,0666);
        if(n<0){
            log("Init false",Error)<<"n:"<<n<<endl;
        }
        log("Init success",Debug)<<"n:"<<n<<endl;
    }
    ~Init(){
        unlink(FIFO_NAME);
        log("remote success",Notice)<<endl;
    }
};

int OpenFIFO(std::string pathname,int flags){
    int fd=open(pathname.c_str(),flags);
    if(fd<0){
        log("open false",Error)<<"fd:"<<fd<<endl;
    }
     log("open success",Debug)<<"fd:"<<fd<<endl;

    return fd;
}

void Wait(int fd){
    log("wait....",Notice)<<endl;
    uint32_t tmp=0; 
    ssize_t s=read(fd,&tmp,sizeof(uint32_t));
    
}

void Signal(int fd)
{
    uint32_t temp = 1;
    ssize_t s = write(fd, &temp, sizeof(uint32_t));
    assert(s == sizeof(uint32_t));
    (void)s;
    log("唤醒中....", Notice) << "\n";
}

void CloseFifo(int fd)
{
    close(fd);
}

Init init;

Log.hpp(日志信息)

#include"common.hpp"

#define Debug   0
#define Notice  1
#define Warning 2
#define Error   3

const std::string msg[] = {
    "Debug",
    "Notice",
    "Warning",
    "Error"
};
std::ostream &log(std::string message,int level){
    std::cout<<"|"<<(unsigned)time(nullptr)<<"|"<<msg[level]<<"|"<<message<<std::endl;
    return std::cout;
}

Makefile

.PHONY:all
all: server client

server:memserver.cc
	g++ -o $@ $^ -std=c++11
client:memclient.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f server client

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值