【Linux】进程间通信

1.进程间通信的目的

进程具有独立性,因此进程间想要通信,成本会非常高,但有时候又需要多个进程协同处理一件事,所以进程间通信是必不可少的

2.管道

从一个进程连接到另一个进程的一个数据流称为一个“管道”

2.1 匿名管道

【Linux】进程间通信_第1张图片

显示器是缺乏管道控制的,所以父子进程在向显示写入的时候是无序的,而管道是自带访问控制机制的,如果管道内部没有数据,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、管道的生命周期   ---- 随进程

2.2 命名管道

继承了匿名管道的所有特性,并且允许两个没有血缘关系的进程进行通信

匿名管道是通过子进程继承父进程实现的,而命名管道是通过不同进程访问同一文件(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<<"客户端->服务器#"<

3.共享内存

创建共享内存 shmget

【Linux】进程间通信_第2张图片

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

你可能感兴趣的:(linux,服务器,运维)