【Linux】进程通信

目录

一、管道通信

二、共享内存

三、消息队列


一、管道通信

管道是由操作系统维护的一个文件,管道通信的本质就是将管道文件作为临界资源,实现不同进程之间的数据读写,但是管道只允许父子进程或者兄弟进程之间的通信。

管道文件本身是全双工机制的,但是在管道通信中,它的工作模式是半双工的,在管道通信之前,读端会关闭对管道文件的写通道,写端会关闭对管道文件的读通道。

当读端在读数据时,若写端关闭,读端会将管道文件中剩余数据读取结束之后再关闭;当写端在写数据时,若读端关闭,写端会被操作系统直接关闭。

那么管道通道如何实现数据交互呢?方法是创建两个管道进行通信。

管道通信方式分为匿名管道命名管道,由 pipe() 系统调用创建并打开,命名管道由 mkfifo() 系统调用创建并由 open() 打开,他们的通信本质是一样的。

// comm.hpp 头文件
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <fcntl.h>
#include <fstream>

#define NAMED_PIPE "./named_pipe"

bool create_fifo(const std::string& path) {
    umask(0);
    int n = mkfifo(path.c_str(), 0666);
    if (n == 0) {
        return true;
    }
    else {
        std::cout << "errno: " << errno << "err string: " << strerror(errno) << std::endl;
        return false; 
    }
}

void remove_fifo(const std::string& path) {
    int n = unlink(path.c_str());
    // unlink 函数功能是删除文件,但会在判断此文件状态之后再删除
    // 若有进程打开此文件,则不会立即删除,等到无进程打开该文件时才会删除
    // 若此文件有多个链接,则进行连接数减一操作
    // 执行成功返回 0,失败返回 -1
    assert(n == 0);
}
// Server
#include "comm.hpp"

int main() {
    std::cout << "server begin" << std::endl;
    int rfd = open(NAMED_PIPE, O_RDONLY);
    if (rfd < 0) {
        bool r = create_fifo(NAMED_PIPE);
        assert(r);
        rfd = open(NAMED_PIPE, O_RDONLY);
    }

    // read
    char buffer[1024];
    while (true) {
        ssize_t s = read(rfd, buffer, sizeof(buffer) - 1);
        if (s > 0) {
            buffer[s] = 0;
            std::cout << "client->server# " << buffer << std::endl;
        }
        else if (s == 0) {
            std::cout << "client quit, me too" << std::endl;
            break;
        }
        else {
            std::cout << "err string: " << strerror(errno) << std::endl;
            break;
        }
    }

    close(rfd);
    std::cout << "server end" << std::endl;

    remove_fifo(NAMED_PIPE);
    return 0;
}
// Client
#include "comm.hpp"

int main() {
    std::cout << "client begin" << std::endl;
    int wfd = open(NAMED_PIPE, O_WRONLY, 0666);
    if (wfd < 0) {
        exit(1);
    }

    // write
    char buffer[1024];
    while (true) {
        std::cout << "please say# ";
        fgets(buffer, sizeof(buffer), stdin);
        if (strlen(buffer) > 0) {
            buffer[strlen(buffer) - 1] = 0;
        }
        if (strcmp(buffer, "quit") == 0) {
            break;
        }
        ssize_t n = write(wfd, buffer, strlen(buffer));
        assert(n == strlen(buffer));
    }

    close(wfd);
    std::cout << "client end" << std::endl;
    return 0;
}

二、共享内存

共享内存是由操作系统维护的一块地址空间,它的工作机制是全双工。

// comm.hpp
#pragma once
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>

#define NAME_PATH "."
#define PROJ_ID 0X66
#define MAX_SIZE 4096

// 创建共享内存时,通过 ftok() 函数获得 key 值保证共享内存在系统中的唯一性
key_t get_key() {
    key_t k = ftok(NAME_PATH, PROJ_ID);
    if (k < 0) {
        std::cout << errno << ": " << strerror(errno) << std::endl;
        exit(1);
    }
    return k;
}

int get_shm_helper(key_t k, int flags) {
    // shmget : 创建共享内存
    int shmID = shmget(k, MAX_SIZE, flags);
    if (shmID < 0) {
        std::cout << errno << ": " << strerror(errno) << std::endl;
        exit(1);
    }
    return shmID;
}

int get_shm(key_t k) {
    return get_shm_helper(k, IPC_CREAT);
}

int create_shm(key_t k) {
    return get_shm_helper(k, IPC_CREAT | IPC_EXCL | 0600);
    // IPC_EXCL 不能单独使用,若共享内存不存在则创建,存在则报错
}

// 删除共享内存
void del_shm(int shmID) {
    // shmctl : 控制共享内存
    if (shmctl(shmID, IPC_RMID, NULL) == -1) {
        std::cout << errno << ": " << strerror(errno) << std::endl;
        exit(1);
    }
}

// 关联共享内存
void* attach_shm(int shmID) {
    void* start = shmat(shmID, NULL, 0);
    if ((long long)start == -1) {
        std::cout << errno << ": " << strerror(errno) << std::endl;
        exit(1);
    }
    return start;
}

// 取消关联
void del_attach_shm(void* start) {
    if (shmdt(start) == -1) {
        std::cout << errno << ": " << strerror(errno) << std::endl;
        exit(1);
    }
}
// Server
#include "comm.hpp"

int main() {
    key_t k = get_key();
    printf("0x%x\n", k);
    int shmID = create_shm(k);      // 创建共享内存
    printf("shmID = %d\n", shmID);

    // 内核为每个共享内存维护一个结构体:struct shmid_ds
    // struct shmid_ds
    // {
    //     struct ipc_perm shm_perm;    /* Ownership and permissions */
    //     size_t          shm_segsz;   /* Size of segment (bytes) */
    //     time_t          shm_atime;   /* Last attach time */
    //     time_t          shm_dtime;   /* Last detach time */
    //     time_t          shm_ctime;   /* Last change time */
    //     pid_t           shm_cpid;    /* PID of creator */
    //     pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
    //     shmatt_t        shm_nattch;  /* No. of current attaches */
    // };

    struct shmid_ds ds;
    shmctl(shmID, IPC_STAT, &ds);
    printf("获取属性: size(%d) pid(%d) myself(%d) key(0x%x)\n",
        ds.shm_segsz, ds.shm_cpid, getpid(), ds.shm_perm.__key);

        
    sleep(2);
    char* start = (char*)attach_shm(shmID);     // 关联共享内存
    printf("attach success, address start: %p\n", start);
    
    // 从共享内存中读取数据
    while (true) {
        if (*start != '\0') {
            printf("clent say# %s\n", start);
            snprintf(start, MAX_SIZE, "\0");    // 打印消息后清空共享内存
        }
    }

    sleep(2);
    del_attach_shm(start);
    return 0;
}
// Client
#include "comm.hpp"

// 查看ipc资源
// 查看:ipcs -m
// 删除:ipcrm -m shmID

int main() {
    key_t k = get_key();
    printf("0x%x\n", k);
    int shmID = get_shm(k);
    printf("%d\n", shmID);

    sleep(2);
    char* start = (char*)attach_shm(shmID);     // 关联共享内存
    printf("attch success, address start: %p\n", start);

    // 将数据写入共享内存
    int count = 1;
    while (true) {
        printf("[please input]# ");
        char message[1024] = {0};
        fgets(message, sizeof(message) - 1, stdin);
        message[strlen(message) - 1] = 0;
        snprintf(start, MAX_SIZE, "%s [pid(%d)][messageID: %d]\n", message, getpid(), count++);
    }

    sleep(2);
    del_attach_shm(start);

    return 0;
}

优点:相比于管道而言,共享内存能够支持任意进程之间的通信,而且访问数据的速度也比管道要快。这得益于通信直接访问内存,而管道则需要先通过操作系统访问文件再获得内存数据。

缺点:用于进程间通信时,共享内存本身不支持阻塞等待操作。这是因为当读端读取数据后,数据并不会在内存中清空。因此读端和写端可以同时访问内存空间,即全双工。因为共享内存本质是进程直接访问内存,无法主动停止读取,如果读端不加以限制,那么将持续读取数据。同理,写端也会持续写入数据。换句话说,共享内存本身没有访问控制。 

三、消息队列

消息队列是由操作系统维护的一个数据结构,遵循队列的FIFO原则,半双工机制。

通过消息队列通信的两个进程也分为读端和写端,读端只负责从消息队列中拿数据,写端只负责向消息队列中写数据。

消息队列的优点是异步性和系统解耦,异步性是指发送消息的进程不需等待接收消息的进程的响应,可以继续执行自己的任务,系统解耦是指发送方和接收方都不关心对方进程的状态,只关注消息的发送和接收。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/shm.h>
#include<fcntl.h>
#include<semaphore.h>

struct msg_buffer {
    long type;		//1表示sender发送的正常数据,2表示sender1发送的结束信号,3表示sender2发送的结束信号
    char text[100];	
};

void *sender1_thread() {
    struct msg_buffer msg;
    char buf[100];
    
    int msqid = msgget((key_t) 2023, 0666|IPC_CREAT);
    if(msqid == -1) {
        printf("消息队列创建失败!");
        exit(-1);
    }
    
    sem_t *mutex = sem_open("mutex", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender1 = sem_open("sender1", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive1 = sem_open("receive1", O_CREAT | O_RDWR, 0666, 0);
    sem_t *next_input = sem_open("next_input", O_CREAT | O_RDWR, 0666, 0);
    
    while(1) {
        sem_wait(mutex);
        sem_wait(next_input);
        printf("sender1 input: ");
        gets(buf);
        
        msg.type = 1;
        if(strcmp(buf, "exit") == 0) {
            strcpy(msg.text, "end1");
            msgsnd(msqid, (void *)&msg, 100, 0);	// 将消息发送到消息队列中
            sem_wait(receive1);
            msgrcv(msqid, (void *)&msg, 100, 2, 0);
            printf("sender1 receive: %s\n\n", msg.text);
            
            sem_post(sender1);
            sem_post(mutex);
            sleep(1);
            return 0;
        }
        else {
            strcpy(msg.text, buf);
            msgsnd(msqid, (void *)&msg, 100, 0);
            sem_post(mutex);
            sleep(1);
        }
    }
}

void *sender2_thread() {
    struct msg_buffer msg;
    char buf[100];
    
    int msgid = msgget((key_t) 2023, 0666|IPC_CREAT);
    if(msgid == -1) {
        printf("消息队列创建失败!");
        exit(-1);
    }
    
    sem_t *mutex = sem_open("mutex", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender2 = sem_open("sender2", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive2 = sem_open("receive2", O_CREAT | O_RDWR, 0666, 0);
    sem_t *next_input = sem_open("next_input", O_CREAT | O_RDWR, 0666, 0);
    
    while(1) {
        sem_wait(mutex);
        sem_wait(next_input);
        printf("sender2 input: ");
        gets(buf);
        
        msg.type = 1;
        if(strcmp(buf,"exit") == 0) {
            strcpy(msg.text, "end2");	
            msgsnd(msgid, (void *)&msg, 100, 0);	
            sem_wait(receive2);
            msgrcv(msgid, (void *)&msg, 100, 3, 0);
            printf("sender2 receive: %s\n\n", msg.text);
            
            sem_post(sender2);
            sem_post(mutex);
            sleep(1);
            return 0;
        }
        else {
            strcpy(msg.text, buf);
            msgsnd(msgid, (void *)&msg, 100, 0);
            sem_post(mutex);
            sleep(1);
        }
    }
}

void *receiver_thread() {
    int finish1 = 0;
    int finish2 = 0;
    struct msg_buffer msg;
    char buf[100];
    
    sem_t *sender1 = sem_open("sender1", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive1 = sem_open("receive1", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender2 = sem_open("sender2", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive2 = sem_open("receive2", O_CREAT | O_RDWR, 0666, 0);
    sem_t *next_input = sem_open("next_input", O_CREAT | O_RDWR, 0666, 0);
    
    int msqid = msgget((key_t) 2023, 0666 | IPC_CREAT);
    if( msqid == -1) {
        printf("create failed");
        exit(-1);
    }
    
    int n;
    while(1){
        if(msgrcv(msqid, (void *)&msg, 100, 0, 0) > 0) {
            printf("Receiver receive: %s\n", msg.text);
            sem_post(next_input);
            
            if(strcmp(msg.text, "end1") == 0) {
                msg.type = 2;
                strcpy(msg.text, "over1");
                n = msgsnd(msqid, (void *)&msg, 100, 0);
                if(n != 0) {
                    sem_post(receive1);
                    sem_wait(sender1);
                }
                finish1 = 1;
            }
            else if(strcmp(msg.text, "end2") == 0) {
            	msg.type = 3;
	    	    strcpy(msg.text, "over2");
            	n = msgsnd(msqid, (void *)&msg, 100, 0);    // 将详细发送到消息队列中
            	if(n != 0) {
                    sem_post(receive2);
                    sem_wait(sender2);
				}
        	finish2 = 1;
            }
        }
        
        if(finish1 == 1 && finish2 == 1) {
            msgctl(msqid, IPC_RMID, 0);	// 删除消息队列
            exit(0);
        }
    }
}


int main() {
	// msgget函数用于创建一个新的消息队列或者获取一个已经存在的消息队列的标识符
    int msqid = msgget((key_t) 2023, 0666 | IPC_CREAT);
    msgctl(msqid, IPC_RMID, 0);	// 删除消息队列,释放相应的系统资源
    sem_unlink("mutex");
    sem_unlink("sender1");
    sem_unlink("sender2");
    sem_unlink("receiver1");
    sem_unlink("receiver2");
    sem_unlink("next_input");
    
    sem_t *mutex = sem_open("mutex", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender1 = sem_open("sender1", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive1 = sem_open("receive1", O_CREAT | O_RDWR, 0666, 0);
    sem_t *sender2 = sem_open("sender2", O_CREAT | O_RDWR, 0666, 0);
    sem_t *receive2 = sem_open("receive2", O_CREAT | O_RDWR, 0666, 0);
    sem_t *next_input = sem_open("next_input", O_CREAT | O_RDWR, 0666, 0);
    
    pthread_t p1, p2, p3;
    if(pthread_create(&p1, NULL, sender1_thread, NULL) != 0) {
        printf("Sender1线程创建失败!");
        exit(-1);
    }
    
    if(pthread_create(&p2, NULL, sender2_thread, NULL) != 0) {
        printf("Sender2线程创建失败!");
        exit(-1);
    }
    
    if(pthread_create(&p3, NULL, receiver_thread, NULL) != 0) {
        printf("receiver线程创建失败!");
        exit(-1);
    }
  
    sem_post(mutex);
    sem_post(next_input);
    
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);
    pthread_join(p3, NULL);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/473824.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis面试题以及答案

1. 什么是Redis&#xff1f;它主要用来什么的&#xff1f; Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并…

虹科Pico汽车示波器 | 免拆诊断案例 | 2019 款东风悦达起亚K2车怠速起停系统工作异常

一、故障现象 一辆2019款东风悦达起亚K2车&#xff0c;搭载G4FG发动机&#xff0c;累计行驶里程约为9 400 km。车主反映&#xff0c;行驶至路口停车等红灯时&#xff0c;怠速起停&#xff08;ISG&#xff09;系统自动使发动机熄火&#xff0c;接着组合仪表提示“怠速起停已解除…

HarmonyOS/OpenHarmony应用开发-DevEco Studio 在MAC上启动报错

报错截图 报错详细内容 ------------------------------------- Translated Report (Full Report Below) -------------------------------------Process: devecostudio [8640] Path: /Applications/DevEco-Studio.app/Contents/MacOS/devecos…

【pip 安装pymssql报错】 Failed to build pymssql

在使用pip install pymssql安装pymssql时报如下图的错误&#xff1b; 报错截图 2&#xff09;查找资料说pip<3.0版本 &#xff0c;我也试了&#xff0c;不行。 你们也可以试一试&#xff1a;pip install"pymssql<3.0" 3&#xff09;我的成功方式&#xff1…

选择word中的表格VBA

打开开发工具 选择Visual Basic插入代码 Sub 选择word中的表格() Dim t As Table an MsgBox("即将选择选区内所有表格&#xff0c;若无选区&#xff0c;则选择全文表格。", vbYesNo, "提示") If an - 6 Then Exit Sub Set rg IIf(Selection.Type wdSel…

【C++】线程库

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;C 前言 C11标准引入了线程库&#xff0c;通过其可以在C程序中方便地创建和管理线程。以下是一些常用的线程库组件&#xff1a; std::thread&#xff1a;std…

【Linux第三课-基础开发工具的使用】yum、vim、gcc/g++编译器、gdb、Make/Makefile编写、进度条程序、git命令行简单操作

目录 yum - 软件包管理器快速认识yum快速使用yumyum搜索yum安装yum卸载 yum的周边 - yum的整个生态问题 vim快速介绍vimvim的模式命令模式插入模式低行模式 常见模式 -- 命令、低行命令模式 -- 光标的移动命令模式 -- 复制粘贴、剪贴、删除命令模式 -- 小写/大写替换模式命令模…

单片机-- 数电(3)

编码器与译码器 译码 &#xff1a;将二进制代码转化为其他进制的代码 编码 &#xff1a;就是将其他代码转换为二进制码 编码器的类型 1二进制编码器 用n位二进制数码对2的n次方个输入信号进行编码的电路 2二-十进制编码器 将0到9十个十进制数转化为二进制代码的电路 2…

PyTorch 深度学习(GPT 重译)(三)

六、使用神经网络拟合数据 本章内容包括 与线性模型相比&#xff0c;非线性激活函数是关键区别 使用 PyTorch 的nn模块 使用神经网络解决线性拟合问题 到目前为止&#xff0c;我们已经仔细研究了线性模型如何学习以及如何在 PyTorch 中实现这一点。我们专注于一个非常简单…

Python 解析CSV文件 使用Matplotlib绘图

数据存储在CSV文件中&#xff0c;使用Matplotlib实现数据可视化。 CSV文件&#xff1a;comma-separated values&#xff0c;是在文件中存储一系列以‘&#xff0c;’分隔的值。 例如&#xff1a;"0.0","2016-01-03","1","3","20…

性能测试-Jmeter中IF控制器使用

一、Jmeter控制器 分为两种类型&#xff1a; 控制测试计划执行过程中节点的逻辑执行顺序&#xff0c;如&#xff1a;循环控制器&#xff0c;if控制器等对测试计划中的脚本进行分组&#xff0c;方便Jmeter统计执行结果以及进行脚本的运行时控制等&#xff0c;如&#xff1a;吞…

【ann2coco】图像label转coco格式的JSON

目录 &#x1f34b;&#x1f34b;需求 &#x1f34b;&#x1f34b;coco格式 &#x1f34b;&#x1f34b;python代码实现 整理不易&#xff0c;欢迎一键三连&#xff01;&#xff01;&#xff01; 送你们一条美丽的--分割线-- &#x1f34b;&#x1f34b;需求 单波段灰度图l…

7-6 混合类型数据格式化输入

题目链接&#xff1a;7-6 混合类型数据格式化输入 一. 题目 1. 题目 2. 输入输出格式 3. 输入输出样例 4. 限制 二、代码 1. 代码实现 #include <stdio.h>int main(void) {int num;char c;float f1,f2;if (scanf("%f %d %c %f", &f1, &num, &c…

【NLP笔记】预训练+微调范式之OpenAI Transformer、ELMo、ULM-FiT、Bert..

文章目录 OpenAI TransformerELMoULM-FiTBert基础结构Embedding预训练&微调 【原文链接】&#xff1a; BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 【本文参考链接】 The Illustrated BERT, ELMo, and co. (How NLP Cracked Tra…

单片机LED灯闪烁

延时函数计算&#xff08;相关代码生成&#xff09;&#xff1a; #include "reg52.h" #include <INTRINS.H> void Delay500ms() //11.0592MHz {unsigned char i, j, k;_nop_();_nop_();i 22;j 3;k 227;do{do{while (--k);} while (--j);} while (--i); }vo…

以太网协议(数据链路层)

一些基础知识: 平时使用的网线,也叫做"以太网线".平时使用的交换机,也叫做"以太网交换机".以太网这个协议,既涉及到数据链路层的内容,也涉及到物理层的内容. 1. 以太网帧的格式 ①目的地址,源地址: 此处的地址,叫做mac地址(物理地址).作用也是区分不同…

水果软件FL Studio 21 for mac 21.2.3.3586破解版的最新版本2024介绍安装

音乐是人类最美好的语言&#xff0c;它能够跨越国界、文化和语言&#xff0c;将人们紧密地联系在一起。在当今数字化时代&#xff0c;音乐创作已经不再是专业人士的专利&#xff0c;越来越多的音乐爱好者开始尝试自己动手制作音乐。而FL Studio21中文版编曲软件正是这样一个为你…

Linux:点命令source

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 source命令用于读取一个文件的内容并在当前Shell环境&#xff08;包括交互式Shell或是非交互式Shell&#xff09;执行里面的命令。它被称为点命令是因为命令名source也可…

【Web应用技术基础】HTML(4)——表单类的标签

目录 题目1&#xff1a;文本框 题目2&#xff1a;密码框 题目3&#xff1a;单选框 题目4&#xff1a;多选框 题目5&#xff1a;单选框选中 题目6&#xff1a;禁用disabled 题目7&#xff1a;lable标签 题目8&#xff1a;下拉框 题目9&#xff1a;textarea 题目10&…

【STM32】读写BKP备份寄存器RTC实时时钟

目录 BKP BKP简介 BKP基本结构 BKP测试代码 RTC RTC简介 RTC框图 RTC基本结构 硬件电路 RTC操作注意事项 接线图 初始化 使用BKP解决只初始化一次时间 初始化参考代码 RTC设置时间 RTC读取时间 完整代码 MyRTC.c MyRTC.h main.c BKP BKP简介 BKP&#xff0…