进程间的IPC通信机制

一、介绍

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

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;
}

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

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

相关文章

Weblogic 任意文件上传漏洞(CVE-2018-2894)

1 漏洞描述 CVE-2018-2894漏洞存在于Oracle WebLogic Server的Web服务测试页面&#xff08;Web Service Test Page&#xff09;中。这个页面允许用户测试Web服务的功能&#xff0c;但在某些版本中&#xff0c;它包含了一个未经授权的文件上传功能。攻击者可以利用这个漏洞&…

变频器通过Modbus转Profinet网关接电机与PLC通讯在自动化的应用

Modbus转Profinet网关&#xff08;XD-MDPN100/300/600&#xff09;的作用是将Modbus协议转换为Profinet协议&#xff0c;支持Modbus RTU主站/从站&#xff0c;并且Modbus转Profinet网关设备自带网口和串口&#xff0c;既可以实现协议转换的同时&#xff0c;也可以实现接口的转换…

ARM架构安全特性之隔离技术

安全之安全(security)博客目录导读 目录 一、保护代码和数据 二、TrustZone 三、安全世界之间的隔离 四、Secure-EL2扩展 五、保护主流计算工作负载 六、领域管理扩展(RME) 七、内存密集型可信应用程序 八、Arm动态TrustZone技术 强制执行明确定义的安全边界是安全工程…

变现 5w+,一个被严重低估的 AI 蓝海赛道,居然用这个免费国产AI工具就能做!

大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 文章首发于公众号&#xff1a;X小鹿AI副业 之前X小鹿一直在各…

HTML与cgi程序的数据交互

1. Html通过ajax获取cgi返回的数据 function HtmlGetCgiData() {$.ajax({type: POST, //提交方法url: cgi-bin/wg67_key_in/wg67_key_in_reflush.cgi, //调用到的cgi程序data: "ajax", //发送的数据&#xff0c;不可缺失该项&#xff0c;不能为空&#xff08;空&…

从“金事通”带给我意想不到的来说--“数据是架构的中心”

背景 上周一个保险的销售人员来找我完成一定的售后流程。其中有一项是请我下载一个叫 金事通的 APP。说实在的我根本没听过。她说这是政治任务。我想不是有你们保险公司的APP了嘛。为什么还要我安装。没办法先安装吧。 经历了注册、人脸识别的步骤后。可以登录了。注册短信发…

AR系列路由器配置本地同一网段互通

A R 路由器是华为公司推出的企业级路由器产品系列&#xff0c;具有高可靠性、高性能和易管理等特点。AR 系列路由器提供的功能包括路由转发、安全接入、语音、视频、无线等多种业务&#xff0c;支持各种接入方式和协议&#xff0c;并且可以方便地进行扩展和升级。 实验拓扑图&…

Spring:@Async注解使用注意事项及九大失效场景

前言 原文作者&#xff1a;微信公众号&#xff1a;苏三说技术 场景举例 代码案例 点击此处可观看&#xff1a;Async注解使用注意事项及九大失效场景

浪潮信息联合SAP助力玉柴集团实现数字化转型的飞跃

数字化时代下&#xff0c;企业面临着前所未有的机遇和挑战。为顺应这一趋势&#xff0c;众多企业纷纷踏上了数字化转型的征程&#xff0c;其中就包括玉柴集团。值得一提的是&#xff0c;在玉柴集团转型过程中&#xff0c;SAP、浪潮信息等国际一流厂商予以了强大的算力支持&…

SSH远程管理 远程访问及控制

SSH远程管理 SSH(Secure Shell) 是一种安全通道协议&#xff0c;主要用来实现字符界面的远程登录、远程复制等功 能。SSH 协议对通信双方的数据传输进行了加密处理&#xff0c;其中包括用户登录时输入的用户口令。与早 期的 Telent (远程登录)、RSH(Remote Shell, 远程执行命…

蓝桥青少一月 STEMA-Python 测评第一题

第一题&#xff08;难度系数 2&#xff0c;18 个计分点&#xff09; (注.input()输入函数的括号中不允许添加任何信息) 编程实现&#xff1a; 给定一个正整数 N&#xff0c;输出 N 除以 3 的商。 输入描述&#xff1a;输入一个正整数 N 输出描述&#xff1a;输出 N 除以 3 的商…

快团团新人怎么找供货团长?免费教程一学就会!

作为快团团的新手&#xff0c;想要寻找供货团长&#xff0c;可以按照以下步骤进行&#xff1a; 打开微信&#xff1a;首先&#xff0c;在您的手机上打开微信应用。 搜索快团团&#xff1a;在微信顶部的搜索框中输入“团长运营之家”&#xff0c;选择出现的“团长运营之家”公号…

纯血鸿蒙APP第三方库——MpChart运动健康场景实践案例

介绍 MpChart是一个包含各种类型图表的图表库&#xff0c;主要用于业务数据汇总&#xff0c;例如销售数据走势图&#xff0c;股价走势图等场景中使用&#xff0c;方便开发者快速实现图表UI&#xff0c;MpChart主要包括线形图、柱状图、饼状图、蜡烛图、气泡图、雷达图、瀑布图…

【JAVA进阶篇教学】第十五篇:Java中AQS讲解

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第十五篇&#xff1a;Java中AQS讲解。 在Java并发编程中&#xff0c;AQS&#xff08;AbstractQueuedSynchronizer&#xff09;是一个重要的框架&#xff0c;用于实现同步器和锁的基础。它提供了一种灵活的方式来实现各种…

SpringSecurity6实现动态权限,rememberMe、OAuth2.0授权登录,退出登录等功能

本文章对应视频可在B站查看SpringSecurity6对应视频教程&#xff0c;记得三连哦&#xff0c;这对我很重要呢&#xff01; 温馨提示&#xff1a;视频与文章相辅相成&#xff0c;结合学习效果更强哦&#xff01; 系列文章链接 1、初识SpringSecurity&#xff0c;认识主流Java权限…

2005-2022年各省共同富裕指数数据(含原始数据+结果)

2005-2022年各省共同富裕指数数据&#xff08;含原始数据结果&#xff09; 1、时间&#xff1a;2005-2022年 2、来源&#xff1a;统计年鉴、各省年鉴 3、范围&#xff1a;31省 4、指标&#xff1a;年份、行政区划代码、地区、人均可支配收入_元、人均消费支出_元、恩格尔系…

ASP.NET Web Api 如何使用 Swagger 管理 API

前言 Swagger 是一个开源的框架&#xff0c;支持 OpenAPI 规范&#xff0c;可以根据 API 规范自动生成美观的、易于浏览的 API 文档页面&#xff0c;包括请求参数、响应示例等信息&#xff0c;并且&#xff0c;Swagger UI 提供了一个交互式的界面&#xff0c;可以帮助我们快速…

STK12 RPO模块学习(2)

一、Coast RPO Sequence 这个序列运行卫星直到它达到了下面三个条件之一。 1&#xff09;截至时间。2)圈数到达了限制。3&#xff09;其他条件&#xff0c;比如近地点。 默认情况下&#xff0c;Astrogator使用“Earth HPOP Default v10”预报器。你能够修改呈其他修改器。下…

C++之map和set 的封装

通过红黑树的学习&#xff08;C之红黑树-CSDN博客&#xff09;让我了解到map和set的底层如何实现&#xff0c;这一次我们来对map和set进行封装。 目录 1.map和set底层原理 2.map和set的定义 3.map和set的仿函数 4.map和set的插入 5.map和set的迭代器 5.1迭代器的构造 5.2…

解决Android手机无法通过蓝牙给win10 PC传送文件

&#xff08;一&#xff09;先配对设备&#xff0c;正常配对就可以 &#xff08;二&#xff09;打开系统设置&#xff0c;win搜索窗口搜索“设置” &#xff08;三&#xff09;搜索“蓝牙” &#xff08;四&#xff09;打开“蓝牙和其他设备”&#xff0c;点击“更多蓝牙设置”…