进程间的IPC通信机制

news2024/11/26 22:37:45

一、介绍

进程与进程间的用户空间相互独立,内核空间共享。

1.传统的进程间通信机制

        a.无名管道 pipe

        b.有名管道 fifo

        c.信号         signal

2.system V中的IPC对象

        a.消息队列 message queue

        b.共享内存 shared memory

        c.信号灯集 semaphoare

3.可用于跨主机传输的通信机制

        a.套接字 socket

二、管道

1.管道可以看成是一个特殊文件,一般文件存储在外存中,而管道内容存储在内存中

2.管道遵循先进先出原则

3.管道的读操作是一次性的,内容被读出后就会从管道中删除

4.管道是一种半双工的通信方式

5.管道只能使用文件IO函数,因为需要直接操作内核空间,如open,close,read,write,但不能使用lseek

6.管道的大小为64k

2.1无名管道

无名管道即在文件系统(用户系统)不可见的管道文件

无名管道不可以用open打开,因为不知道路径以及名字

无名管道只能用于具有亲缘关系的进程间通信。由于无名管道在文件系统中不可见,两个无关的进程,无法拿到同一根管道的读写段,只有具有亲缘关系的进程,在父进程中创建一根管道,拿到读写端后,调用fork函数,创建出来的子进程也会有该管道的读写端。

从管道中读取数据:
1.读写端均存在时,当管道中没有数据时,read会阻塞

2.当管道的写端不存在时,若管道中有数据,会先将数据读取完毕,没有数据时,read函数不会阻塞,直接返回0

向管道中写入数据:

1.读写段均存在时,write会阻塞

2.当管道的读端不存在时,调用write函数,尝试向管道中写入数据会导致管道破裂。

#include <head.h>
int main(int argc, char const *argv[])
{
    // 管道的创建必须放在fork前
    // 若放在fork后,会导致父子进程各自创建一个内管道,无法通信
    int pfd[2] = {0};
    if (pipe(pfd) < 0) // pf[0]为读的文件描述符,pf[1]为写的文件描述符
    {
        perror("pipe");
        return -1;
    }
    printf("管道创建成功\n");

    pid_t pid = fork();
    if (pid > 0)
    {
        // 父进程发数据给子进程
        char buf[128] = "";
        while (1)
        {
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf) - 1] = 0; // 从终端获取数据,将最后获取到的\n变成\0

            // 将数据写入到管道中
            if (write(pfd[1], buf, sizeof(buf)) < 0)
            {
                perror("write");
                return -1;
            }
            printf("写入成功\n");
        }
    }
    else if (pid == 0)
    {
        // 子进程接收父进程发送过来的数据
        char buf[128] = "";
        int res = 0;
        while (1)
        {
            // 管道中没有数据时,read会阻塞
            res = read(pfd[0], buf, sizeof(buf));
            printf("读取成功\n");
            printf("%s\n", buf);
        }
    }
    else
    {
        perror("fork");
        return -1;
    }
    return 0;
}

 2.2有名管道

写端

#include <head.h>
int main(int argc, char const *argv[])
{
    umask(0);
    // 创建有名管道
    if (mkfifo("./myfifo", 0664) < 0)
    {
        if (errno != 17)
        { // 文件已存在的错误是一个合法的错误,需要排除,代码允许正常运行
            perror("mkfifo");
            return -1;
        }
    }
    printf("有名管道创建成功\n");

    // 以只写的方式打开有名管道
    int fd = open("./myfifo", O_WRONLY); // 当只有一个写端时,open函数会阻塞
    if (fd < 0)
    {
        perror("open");
        return -1;
    }
    printf("open succcess\n");

    char buf[128] = "";
    while (1)
    {
        printf("请输入>>>");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = 0;
        if (write(fd, buf, sizeof(buf)) < 0)
        {
            perror("write");
            return -1;
        }
        if (strcmp(buf, "quit") == 0)
            break;
        printf("写入成功\n");
    }
    close(fd);
    return 0;
}

读端

#include <head.h>
int main(int argc, char const *argv[])
{
    umask(0);
    // 创建有名管道
    if (mkfifo("./myfifo", 0664) < 0)
    {
        if (errno != 17)
        { // 文件已存在的错误是一个合法的错误,需要排除,代码允许正常运行
            perror("mkfifo");
            return -1;
        }
    }
    printf("有名管道创建成功\n");

    // 以只读的方式打开有名管道
    int fd = open("./myfifo", O_RDONLY); // 当只有一个读端时,open函数会阻塞
    if (fd < 0)
    {
        perror("open");
        return -1;
    }
    printf("open succcess\n");

    char buf[128] = "";
    int res = 0;
    while (1)
    {
        bzero(buf, sizeof(buf));
        res = read(fd, buf, sizeof(buf));
        if (res < 0)
        {
            perror("read");
            return -1;
        }
        else if ((res == 0) || strcmp(buf, "quit") == 0)
        {
            printf("写端退出\n");
            break;
        }
        printf("buf=%s\n", buf);
    }
    close(fd);
    return 0;
}

三、信号

原理

信号是一种异步通信的方式

异步:任务与任务之间无关系,根据CPU轮询机制来运行多个任务

同步:任务与任务之间有先后关系,必须要等任务A结束2后才能执行任务B

1.signal

#include <head.h>

void handler(int sig)
{
    printf("sig=%d\n", sig);
    return;
}
int main(int argc, char const *argv[])
{
    // 捕获2)SIGINT信号
    if (signal(2, handler) == SIG_ERR)
    //第一个参数:指定要捕获的信号,天对应的编号或宏
    //第二个参数:可以填SIG_IGN:忽略信号
                    //SIG_DFL:执行默认操作
                    //捕获信号信号:填写函数指针变量
    {
        perror("signal");
        return -1;
    }
    printf("捕获信号成功\n");
    while (1)
    {
        printf("主函数\n");
        sleep(1);
    }
    return 0;
}

 

练习:用信号的方式回收僵尸进程

#include <head.h>
int count = 0;

void handler(int sig)
{
    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
}
int main(int argc, char const *argv[])
{
    // 捕获17)SIGCHLD
    __sighandler_t s = signal(17, handler);
    if (SIG_ERR == s)
    {
        perror("signal");
        return -1;
    }
    int i = 0;
    while (i < 100)
    {
        int res = fork();
        if (res == 0)
            exit(0);
        i++;
    }
    while (1)
        sleep(1);

    return 0;
}

2.kill

当收到quit时,父子进程全部退出

#include <head.h>
int main(int argc, char const *argv[])
{
    pid_t pid = fork();
    if (pid > 0)
    {
        while (1)
        {
            printf("父进程 %d %d\n", getpid(), pid);
            sleep(1);
        }
    }
    else if (pid == 0)
    {
        char buf[128] = "";
        bzero(buf, sizeof(buf));
        while (1)
        {
            printf("请输入>>>");
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf) - 1] = 0;
            printf("buf=%s", buf);
            if (strcmp(buf, "quit") == 0)
            {
                break;
            }
        }
        // 子进程给父进程发信号,请求父进程退出
        kill(getppid(), 9); // 9号是杀死信号
    }
    else
    {
        perror("fork");
        return -1;
    }

    return 0;
}

3.alarm

设置3秒的定时器

#include <head.h>
void callback(int sig)
{
    printf("超时了\n");
    alarm(3);
    return;
}
int main(int argc, char const *argv[])
{
    // 捕获14号信号
    if (signal(14, callback) == SIG_ERR)
    {
        perror("signal");
        return -1;
    }
    alarm(3);
    while (1)
    {
        printf("signal....\n");
        sleep(1);
    }
    return 0;
}

四、消息队列

消息队列按照先进先出的原则,但也可以限制消息类型读取

消息队列独立于进程,等进程结束后,消息队列以及其中的内容不会删除,除非重启操作系统或手动删除

发送数据

#include <head.h>
struct msgbuf
{
    long mtype;      // 消息类型,必须大于0
    char mtext[128]; // 消息内容
};
int main(int argc, char const *argv[])
{
    // 创建key值
    key_t key = ftok("/home/ubuntu/", 1);
    // 第二个参数:填非0
    if (key < 0)
    {
        perror("ftok");
        return -1;
    }

    // 创建消息队列
    int msqid = msgget(key, IPC_CREAT | 0664);
    if (msqid < 0)
    {
        perror("msgget");
        return -1;
    }
    struct msgbuf sndbuf;
    while (1)
    {
        printf("请输入人消息类型>>>");
        scanf("%ld", &sndbuf.mtype);
        getchar();

        if (sndbuf.mtype == 0)
        { // 若输入类型为0,退出循环
            break;
        }
        printf("请输入消息内容>>>");
        fgets(sndbuf.mtext, sizeof(sndbuf.mtext), stdin);
        sndbuf.mtext[strlen(sndbuf.mtext) - 1] = 0;

        // 向消息队列中发送数据
        if (msgsnd(msqid, &sndbuf, sizeof(sndbuf.mtext), 0) < 0)
        {
            perror("msgsnd");
            return -1;
        }
        printf("发送成功\n");
    }
    // 删除消息队列
    if (msgctl(msqid, IPC_RMID, NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    printf("删除消息队列成功\n");
    return 0;
}

 

接收数据

#include <head.h>
struct msgbuf
{
    long mtype;      // 消息类型,必须大于0
    char mtext[128]; // 消息内容
};
int main(int argc, char const *argv[])
{
    // 创建key值
    key_t key = ftok("/home/ubuntu/", 1);
    // 第二个参数:填非0
    if (key < 0)
    {
        perror("ftok");
        return -1;
    }

    // 创建消息队列
    int msqid = msgget(key, IPC_CREAT | 0664);
    if (msqid < 0)
    {
        perror("msgget");
        return -1;
    }
    struct msgbuf recvbuf;
    int res = 0;
    while (1)
    {
        // 从指定的消息队列中读取数据
        // 读取消息队列中的第一条消息,先进先出
        res = msgrcv(msqid, &recvbuf, sizeof(recvbuf.mtext), 0, IPC_NOWAIT);
        if (res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("接收到的消息为%s\n", recvbuf.mtext);
    }
    // 删除消息队列
    if (msgctl(msqid, IPC_RMID, NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    printf("删除消息队列成功\n");
    return 0;
}

五、共享内存

共享内存是最高效的进程间通信方式

多个进程可以同时访问共享内存,修改其中的内容,所以共享内存其实是临界资源,需要注意进程间的同步互斥

共享内存独立于进程,等进程结束后,共享内存以及其中的内容不会删除,除非重启操作系统或者手动删除

发送数据

#include <head.h>
int main(int argc, char const *argv[])
{
    // 创建key值
    key_t key = ftok("./", 10);
    if (key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key=%#x\n", key);

    // 创建共享内存,获得shmid号
    int shmid = shmget(key, 32, IPC_CREAT | 0664);
    if (shmid < 0)
    {
        perror("shmget");
        return -1;
    }
    printf("shmid=%d\n", shmid);
    // 映射共享内存到用户空间
    void *addr = shmat(shmid, NULL, 0);
    if (addr == (void *)-1)
    {
        perror("shmat");
        return -1;
    }

    // 先往共享内存中存储一个int类型的数据
    *(int *)addr = 10;

    // 再往int类型数据后面存储一个字符串
    char *ptr = (char *)addr + 4;

    strcpy(ptr, "hello world");

    return 0;
}

接收数据

#include <head.h>
int main(int argc, char const *argv[])
{
    // 创建key值
    key_t key = ftok("./", 10);
    if (key < 0)
    {
        perror("ftok");
        return -1;
    }

    // 创建共享内存,获得shmid号
    int shmid = shmget(key, 32, IPC_CREAT | 0664);
    if (shmid < 0)
    {
        perror("shmget");
        return -1;
    }

    // 映射共享内存到用户空间
    void *addr = shmat(shmid, NULL, 0);
    if (addr == (void *)-1)
    {
        perror("shmat");
        return -1;
    }

    printf("%d\n", *(int *)addr);
    printf("%s\n", (char *)((int *)addr + 1));

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++ 】红黑树

1.1 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍&#xff…

Android的NDK开发中Cmake报缺少对应的x86的so文件

需要实现一个串口操作的命令。 供应商提供了2个so文件。 分别是 armeabi-v7a 和 arm64-v8a 添加到对应的cpp下。 在CMakeLists.txt里添加so文件 # 添加预编译的库 add_library(libxxx SHARED IMPORTED)# 设置库的路径 set_target_properties(libxxx PROPERTIES IMPORTED_…

OPT系列极速版远距离光数据传输器|光通讯传感器安装与调试方法

OPT系列极速版远距离光数据传输器|光通讯传感器使用红外激光通信&#xff0c;满足全双工 100M 带宽&#xff0c;通讯距离可达 300 米。能够快速&#xff0c;稳地传送数据&#xff0c;支持主流的工业控制总线&#xff08;Profinet&#xff0c;Ethercat 等&#xff09;&#xff1…

如何8步完成hadoop单机安装

前言 Hadoop是一个开源框架&#xff0c;用于存储和处理大规模数据集。 系统要求 Ubuntu 20.044GB&#xff08;建议8GB&#xff09;hadoop-3.3.6 步骤1&#xff1a;更新系统 打开终端并输入以下命令来更新您的系统&#xff1a; apt update 步骤2&#xff1a;安装Java Had…

浪潮信息企业级存储逆势增长 市场份额位列中国前二

2023年&#xff0c;中国企业级存储市场竞争激烈&#xff0c;在挑战重重之下&#xff0c;浪潮信息仍然实现逆势增长&#xff0c;销售额增幅达4.7%&#xff0c;市场份额相比2022年扩大0.6%&#xff0c;位列中国前二。另外&#xff0c;在高端和全闪存阵列细分市场&#xff0c;浪潮…

文件操作IO网络编程网络原理

​ 文件操作—IO 文件在计算机中可以代表很多东西 在操作系统中, 文件主要是指硬盘文件 硬盘主要分为机械硬盘和固态硬盘。机械硬盘通过磁头在旋转的磁盘上读取数据&#xff0c;适合顺序读取。而固态硬盘则使用闪存芯片来存储数据&#xff0c;没有机械部件&#xff0c;因此读…

进程创建-fork

demo1代码状态变迁 demo2代码输出到stdout使用管道 demo1 代码 #include <pthread.h> #include <iostream> #include <unistd.h> int main(int argc, char const *argv[]) {// 1.pid_t x fork();// 2.pid_t y fork();// 3.printf("%d %d\n", x…

电机及FOC算法介绍

一.电机概述 1.电机的简介 电机是一种可以在电能和机械能的之间相互转换的设备&#xff0c;其中发电机是将机械能转换为电能&#xff0c;电动机是将电能转换为机械能。发电机的主要用于产生电能&#xff0c;用途单一&#xff0c;但是电动机主要用于产生机械能&#xff0c;用途…

内容与图像一对多问题解决

场景复现 分析&#xff1a; 其实这是两给表&#xff0c;一个内容表&#xff0c;一个图片表&#xff0c;一对多的关系。 解决思路: 1. 先上传图片拿到图片的List集合ids&#xff0c;返回值是集合的ids&#xff0c;给到前端 2. 再添加内容表的数据生成了id&#xff0c;遍历查…

【Python探索之旅】初识Python

目录 发展史&#xff1a; 环境安装&#xff1a; 入门案例&#xff1a; 变量类型 标准数据类型 数字类型&#xff1a; 字符串&#xff1a; 全篇总结&#xff1a; 前言&#xff1a; Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设…

提升网络性能,解决网络故障,了解AnaTraf网络流量分析仪

在当今数字化时代&#xff0c;网络性能监测与诊断(Network Performance Monitoring and Diagnosis,NPMD)成为了企业和个人关注的焦点。随着网络流量不断增长&#xff0c;确保网络的稳定性和高效性变得更加重要。在这个领域&#xff0c;AnaTraf网络流量分析仪是您不可或缺的得力…

SqlServer2016安装

1、下载 下载地址&#xff1a; https://www.microsoft.com/en-us/server-cloud/products/sql-server-2016/ 或者 MSDN, 我告诉你 - 做一个安静的工具站 开发版下载地址&#xff1a;https://myprodscussu1.app.vssubscriptions.visualstudio.com/downloads KB2919442下载地址…

一、数据结构的三要素

数据的存储结构&#xff1a;顺序&#xff08;物理位置相邻&#xff09;、链式&#xff08;物理位置不相邻&#xff09;、索引&#xff08;还需要建立索引表&#xff09;、散列&#xff08;根据关键字直接计算出该元素的存储地址又称为hash存储&#xff09;、 时间复杂度&#x…

数据库——SQL SERVER(先学删库跑路)

目录 一&#xff1a;什么是数据库 二&#xff1a;为什么需要数据库 三&#xff1a;数据库的安装 四&#xff1a;学前必备知识 1. 数据库原理 2. 数据库与编程语言 3. 数据库与数据结构的区别 4. 连接 5. 有了编程语言为啥还要数据库 6. 初学者学习数据库的三个方面 …

数据结构与算法学习笔记六-二叉树的链式存储表示法和实现(C语言)

目录 前言 1.二叉树的链式存储 2.二叉链表的表示和实现 1.定义 2.创建 4.中序遍历二叉树 5.后序遍历二叉树 6.后序遍历二叉树 7.完整代码 前言 这篇博客主要介绍二叉树的链式存储结构。 1.二叉树的链式存储 上篇文章中介绍了二叉树的顺序存储结构&#xff0c;在最坏的…

MySQL_DDL语句

1.Data类临时数据的弊端 我们之前在将ServletJSP配合处理请求的过程中 数据库起到一个存取数据的作用 但是我们之前的案例中 数据是在Data类中临时定义的 并不是从数据库中获取的 这样做是不好的 因为每一次服务器关闭之后 那么部署在其上的类也会随着卸载 紧接着和类相挂钩的静…

DS高阶:B树系列

一、常见的搜索结构 1、顺序查找 时间复杂度&#xff1a;O(N) 2、二分查找 时间复杂度&#xff1a;O(logN) 要求&#xff1a;&#xff08;1&#xff09;有序 &#xff08;2&#xff09;支持下标的随机访问 3、二叉搜索树&#xff08;BS树&#xff09; 时间复杂…

通俗易通解读Restaking,潜力如何?(bitget钱包玩转)

关于 Restaking 再质押&#xff1a; 1. Restaking 在功能上&#xff0c;将以太坊安全性标准化&#xff0c;并将其“货币化”&#xff0c;Restaking 出售的就是以太坊的安全性&#xff0c;同时&#xff0c;将安全性用质押后的通证数量直观表现出来 2. Restaking 在经济机制上的逐…

NSSCTF | [第五空间 2021]WebFTP

注意看这里的题目标签&#xff0c;目录扫描&#xff0c;.git泄露。那么这道题虽然打开是一个登录的界面&#xff0c;但是并不是我们熟悉的爆破和SQL注入。 但是可以在题目标签上看到目录扫描&#xff0c;我们就用dirsearch扫一扫看看 python dirsearch.py -u http://node4.ann…

下载wsl 网络出现问题,解决办法

查看能下载的wsl系统时&#xff0c;显示网络出现问题 解决办法&#xff1a;更换网络节点 最终效果&#xff1a;