Linux Day12 ---进程间通信

news2024/11/28 5:26:46

一、管道

1.1 有名管道

有名管道可以在任意两个进程之间通信

1.1.1 有名管道的创建:

命令创建: mkfifo +管道名
系统调用创建

1.1.2 与普通文件区别

打开管道文件,在内存分配一块空间,往管道文件里面写数据,实际是写入内存,读取也是,这样比较高效,所以管道文件的大小永远为0.

1.1.3 使用有名管道

管道文件只有两种打开方式:只读 O_RDONLY 只写 O_WRONLY

一个进程往管道里面写入数据

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<assert.h>
int main()
{
    int fdw=open("./FIFO",O_WRONLY);
    assert(fdw!=-1);
    printf("fdw=%d\n",fdw);
    while(1)
    {
        printf("input:\n");
        char buff[128]={0};
        fgets(buff,128,stdin);
        if(strncmp(buff,"end",3)==0)
        {
            break;
        }
        write(fdw,buff,strlen(buff));
    }
    close(fdw);
}

另外一个进程从管道里面读出数据

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
int main()
{
    int fdw=open("./FIFO",O_RDONLY);
    assert(fdw!=-1);
    printf("fdw=%d\n",fdw);
    while(1)
    {
        char buf[128]={0};
        if(read(fdw,buf,127)==0)
        {
            break;
        }
        printf("read=%s\n",buf);
    }
    close(fdw);
}

结果

注意

需要两个进程同时进行,至少一个读一个写,保证读和写都得有。

当写端关闭,读端自动返回0。

如果读端关闭,系统会给写端发送信号--13,引起异常。

加上这段代码,当读端结束后,写端会打印系统返回的信号。

1.2 无名管道

无名管道主要应用于父子进程间的通信

1.2.1 创建无名管道

int pipe(int fds[2]);
pipe()成功返回 0,失败返回-1
fds[0]是管道读端的描述符
fds[1]是管道写端的描述符
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
int main()
{
    int fd[2];
    assert(pipe(fd)!=-1);
    pid_t pid =fork();
    assert(pid!=-1);
    if(pid ==0)
    {
        close(fd[1]);
        char buff[128]={0};
        read(fd[0],buff,127);
        printf("child read:%s\n",buff);
        close(fd[0]);
    }
    else
    {
        close(fd[0]);
        write(fd[1],"hello",5);
        close(fd[1]);
    }
}
结果

半双工,单工,全双工

单工:方向固定

半双工:一次只允许一方到另一方传递

全双工:允许同时进行双方传递

1.3 管道实现

二、信号量

信号量是一个特殊的变量,一般取正数值。它的值代表允许访问的资源数目,获取资源时,需要对信号量的值进行原子减一,该操作被称为 P 操作。当信号量值为 0 时,代表没有 资源可用,P 操作会阻塞。释放资源时,需要对信号量的值进行原子加一,该操作被称为 V 操作。信号量主要用来同步进程。信号量的值如果只取 0,1,将其称为二值信号量。如果信 号量的值大于 1,则称之为计数信号量。
当信号量取0和1:二值信号量
当信号量取3和5:计数信号量
临界资源:同一时刻,只允许被一个进程或线程访问的资源
临界区:访问临界资源的代码段

2.1 没有信号量控制

A和B去打印,需求是A,B同时打印次数为偶数

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
int main()
{
    for(int i=0;i<5;i++)
    {
        printf("A");
        fflush(stdout);
        int n=rand()%3;
        sleep(n);
        printf("A");
        fflush(stdout);
        n=rand()%3;
        sleep(n);
    }
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
int main()
{
    for(int i=0;i<5;i++)
    {
        printf("B");
        fflush(stdout);
        int n=rand()%3;
        sleep(n);
        printf("B");
        fflush(stdout);
        n=rand()%3;
        sleep(n);
    }
}

结果

在B未结束时A已经出现了,所以表示一个进程未结束另一个进程已经开始了。

2.2 信号量的封装

为解决2.1的问题,我们增加一个信号量,值为1,当执行A时p操作-1,值为0,这时表明没有资源可以使用,进程B无法运行,当A结束后v操作+1,这时进程B就可以有资源去使用,执行B资源然后同上,这样就解决了两个进程依次执行的

2.2.1 介绍一些函数

2.2.1.1 semget()

int semget(key_t key, int nsems, int semflg);
semget()创建或者获取已存在的信号量
semget()成功返回信号量的 ID, 失败返回-1
key:两个进程使用相同的 key 值,就可以使用同一个信号量
nsems:内核维护的是一个信号量集,在新建信号量时,其指定信号量集中信号
量的个数
semflg 可选: IPC_CREAT IPC_EXCL

2.2.1.2 semop()

int semop( int semid, struct sembuf *sops, unsigned nsops);
semop()对信号量进行改变,做 P 操作或者 V 操作
semop()成功返回 0,失败返回-1
struct sembuf
{
      unsigned short sem_num; //指定信号量集中的信号量下标
      short sem_op; //其值为-1,代表 P 操作,其值为 1,代表 V 操作
      short sem_flg; //SEM_UNDO
}

2.2.1.3 semget()

int semctl( int semid, int semnum, int cmd, ...);
semctl()控制信号量
semctl()成功返回 0,失败返回-1 
cmd 选项: SETVAL IPC_RMID
 
  union semun
    {
      int val;
      struct semid_ds *buf;
      unsigned short *array;
      struct seminfo *_buf;
    };

2.2 实现创建一个信号量

#include "sem.h"

static int semid = -1;
void sem_init()
{
    semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//全新创建信号量,如果存在就失败
    if ( semid == -1 )//失败,表示已存在
    {
        semid = semget((key_t)1234,1,0600);//获取已存在的信号量id
        if ( semid == -1)
        {
            printf("semget err\n");
        }
    }
    else//全新创建成功,那么要进行初始化
    {
        union semun a;
        a.val = 1;//信号量的初始值
        if ( semctl(semid,0,SETVAL,a) == -1)//设置初始值
        {
            printf("semctl err\n");
        }
    }
}
void sem_p()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;//p
    buf.sem_flg = SEM_UNDO;

    if ( semop(semid,&buf,1) == -1)
    {
        printf("semop p err\n");
    }
}
void sem_v()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = 1;//v
    buf.sem_flg = SEM_UNDO;

    if ( semop(semid,&buf,1) == -1)
    {
        printf("semop v err\n");
    }
}
void sem_destroy()
{
    if ( semctl(semid,0,IPC_RMID) == -1)
    {
        printf("semctl destroy err\n");
    }
}

给2.1中的a.c b.c 添加信号量就会输出下图

三、共享内存

两个进程在进行通讯时,共同使用同一块内存。

3.1 介绍一些函数

3.1.1 shemget()创建共享内存

int shmget(key_t key, size_t size, int shmflg);
shmget()用于创建或者获取共享内存
shmget()成功返回共享内存的 ID, 失败返回-1
key: 不同的进程使用相同的 key 值可以获取到同一个共享内存
size: 创建共享内存时,指定要申请的共享内存空间大小
shmflg: IPC_CREAT IPC_EXCL

3.1.2 shmat() 用来创建映射

void * shmat( int shmid, const void *shmaddr, int shmflg);
shmat()将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上
shmat()成功返回返回共享内存的首地址,失败返回 NULL
shmaddr:一般给 NULL,由系统自动选择映射的虚拟地址空间
shmflg: 一般给 0, 可以给 SHM_RDONLY 为只读模式,其他的为读写

3.1.3  shmdt()用来断开映射

int shmdt( const void *shmaddr);
shmdt()断开当前进程的 shmaddr 指向的共享内存映射
shmdt()成功返回 0, 失败返回-1

3.1.4 shmctl()用来控制共享内存

int shmctl( int shmid, int cmd, struct shmid_ds *buf);
shmctl()控制共享内存
shmctl()成功返回 0,失败返回-1
cmd: IPC_RMID 32. *

3.2 实现共享内存

这里的共享内存的实现方式个人感觉和无名管道很像,都是使用两个信号量来控制输入输出进程的执行,只不过无名管道的信号量是来源于fork()复制来的。

这个是进程1的实现,主要就是往共享内存里面传输数据

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
#include "sem.h"

int main()
{
    int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);
    if ( shmid == -1 )
    {
        printf("shmget err\n");
        exit(1);
    }

    char* s = (char*)shmat(shmid,NULL,0);
    if ( s == (char*)-1) 
    {
        printf("shmat err\n");
        exit(1);
    }   

    sem_init();
    while( 1 )
    {
        printf("input\n");
        char buff[128] = {0};
        fgets(buff,128,stdin);

        sem_p(SEM1);
        strcpy(s,buff);
        sem_v(SEM2);

        if ( strncmp(buff,"end",3) == 0)
        {
            break;
        }
    }

    shmdt(s);
}

在进程1结束后我们只用断开进程1和共享内存之间的映射就行,不需要结束共享内存,因为后续还会有其他进程使用这个共享内存。

这个是进程2的实现方式,主要就是从共享内存中读出数据

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
#include "sem.h"

int main()
{
    int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);
    if ( shmid == -1 )
    {
        printf("shmget err\n");
        exit(1);
    }

    char * s = (char*)shmat(shmid,NULL,0);
    if ( s == (char*)-1)
    {
        printf("shmat err\n");
        exit(1);
    }

    sem_init();
    while( 1 )
    {
        sem_p(SEM2);
        if ( strncmp(s,"end",3) == 0 )
        {
            break;
        }

        printf("read:%s\n",s);
        sem_v(SEM1);
    }

    shmdt(s);
    shmctl(shmid,IPC_RMID,NULL);
    sem_destroy();
}

这里如果直接去实现就会无限迅速读入读出,所以需要我们引入信号量加以管制,和2的代码很像,只需要大家看得懂我前面画的图,实现这个功能并不复杂。

四、信息队列

根据前面的数字类型进行分类。

4.1 认识一些函数接口

4.1.1 msgget() 获取消息队列

int msgget(key_t key, int msqflg);
msgget()创建或者获取一个消息队列
msgget()成功返回消息队列 ID,失败返回-1
msqflg: IPC_CREAT

 4.1.2 msgsnd()发送信息

int msgsnd( int msqid, const void *msqp, size_t msqsz, int msqflg);
msgsnd()发送一条消息,消息结构为:
struct msgbuf
{
    long mtype; // 消息类型, 必须大于 0  必须有
    char mtext[1]; // 消息数据
};
 msgsnd()成功返回 0, 失败返回-1
 msqsz: 指定 mtext 中有效数据的长度
 msqflg:一般设置为 0 可以设置 IPC_NOWAIT

 4.1.3 msgrcv()接收消息

ssize_t msgrcv( int msqid, void *msgp, size_t msqsz, long msqtyp, int msqflg); 
msgrcv()接收一条消息 
msgrcv()成功返回 mtext 中接收到的数据长度, 失败返回-1
msqtyp: 指定接收的消息类型,类型可以为 0
msqflg: 一般设置为 0 可以设置 IPC_NOWAIT

 4.1.4 msgctl()控制消息队列

int msgctl( int msqid, int cmd, struct msqid_ds *buf);
msgctl()控制消息队列
msgctl()成功返回 0,失败返回-1
cmd: IPC_RMID
 

 4.2 示例

进程 a 发送一条消息,进程 b 读取消息。
进程a
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/msg.h>
struct message
{
    long int type;//固定
    char buff[32];
};
int main()
{
    int msgid=msgget((key_t)1234,IPC_CREAT|0600);
    if(msgid==-1)
    {
        printf("msgget err\n");
        exit(1);
    }
    struct message dt;
    dt.type=1;
    strcpy(dt.buff,"hello1");
    msgsnd(msgid,&dt,32,0);
    
}

 进程b

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/msg.h>
struct message
{
    long int type;//固定
    char buff[32];
};
int main()
{
    int msgid=msgget((key_t)1234,IPC_CREAT|0600);
    if(msgid==-1)
    {
        printf("msgget err\n");
        exit(1);
    }
    struct message dt;
    msgrcv(msgid,&dt,32,1,0);//0代表不区分类型
    printf("read message:%s\n",dt.buff);

}

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

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

相关文章

产品思维用户思维

用户思维是一种关注用户需求、体验和价值的思维方式,将用户放在产品设计、开发和提供服务的核心位置。它强调了理解用户在不同场景下的需求,提供与之相匹配的解决方案,从而帮助用户实现他们的目标。 描述一个用户时,可以从不同角度来考虑: 按人口属性描述用户: 个人属性…

【python】reshape的使用

import numpy as np x np.array([1,2,3]) print(fx.shape{x.shape}) print(fx.reshape((1,-1)){x.reshape((1,-1))}) print(fx.reshape(3,){x.reshape(3,)}) print(fx.reshape(3,1)\n{x.reshape(3,1)}) print(fx[:,np.newaxis]\n{x[:,np.newaxis]})

IDEA中Run/Debug Configurations添加VM options和Program arguments

1. 现象描述 我在我的IDEA当中打开配置模板后&#xff0c;发现没有VM options和Program arguments&#xff0c;也就是虚拟机选项和程序实参这两项&#xff0c;导致我不能配置系统属性参数和命令行参数&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff0…

Navicat连接数据库报2003错误解决办法

是防火墙还没有开启 查看防火墙管理的端口 设置3306防火墙开启&#xff0c;重载防火墙 连接成功

java实现粤语歌曲0243填词法

粤语歌曲填词法 一、前言 转化成数字歌。对每个音符&#xff0c;提供配合广东话声调的字&#xff0c;选出成为歌词。可以在网上创作&#xff0c;或下载到自己电脑中使用。 简谱 3656536&#xff0c;歌词 落花满天蔽月光。 唱起来配合乐曲音调。这叫做‘叶韵’&#xff0c;又叫…

基于Open3D的点云处理17-Open3d的C++版本

参考&#xff1a; http://www.open3d.org/docs/latest/cpp_api.htmlhttp://www.open3d.org/docs/latest/getting_started.html#chttp://www.open3d.org/docs/release/cpp_project.html#cplusplus-example-projecthttps://github.com/isl-org/open3d-cmake-find-packagehttps:/…

数学建模:相关性分析

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 数学建模&#xff1a;相关性分析 文章目录 数学建模&#xff1a;相关性分析相关性分析两变量的相关分析PearsonSpearmanKendall tua-b 双变量关系强度测量的指标相关系数的性质代码实现example偏相关分析 相…

vmware虚拟机(ubuntu)远程开发golang、python环境安装

目录 1. 下载vmware2. 下载ubuntu镜像3. 安装4. 做一些设置4.1 分辨率设置4.2 语言下载4.3 输入法设置4.4 时区设置 5. 直接切换管理员权限6. 网络6.1 看ip6.2 ssh 7. 本地编译器连接远程服务器7.1 创建远程部署的配置7.2 文件同步7.3 远程启动项目 8. ubuntu安装golang环境8.1…

2023 AZ900备考

文章目录 如何学习最近准备考AZ900考试&#xff0c;找了一圈文档&#xff0c;结果发现看那么多文档&#xff0c;不如直接看官方的教程https://learn.microsoft.com/zh-cn/certifications/exams/az-900/ &#xff0c;简单直接&#xff0c;突然想到纳瓦尔宝典中提到多花时间进行思…

JUC并发编程--------CAS、Atomic原子操作

什么是原子操作&#xff1f;如何实现原子操作&#xff1f; 什么是原子性&#xff1f; 事务的一大特性就是原子性&#xff08;事务具有ACID四大特性&#xff09;&#xff0c;一个事务包含多个操作&#xff0c;这些操作要么全部执行&#xff0c;要么全都不执行 并发里的原子性…

Java8新特性Lambda表达式详细

Comparator&#xff1a;此接口中只包含一个方法 int compare(T A,T B) 如果A>B&#xff0c;返回正数 如果AB&#xff0c;返回0 入伙A<B&#xff0c;返回负数Lambda表达式 函数式重点&#xff1a;只需要关注参数列表和方法体 看参数ctrlp 需求分析 我们在创建线程并启动…

Python常用IDE选择与安装

1、IDE简介 选择一款高效而又顺手的IDE学习或使用Python&#xff0c;可以让你的开发之路充满激情和动力&#xff0c;让你真正投入其中。 常见的Python的IDE工具有&#xff1a; PyCharm 由JetBrains开发的Python IDE&#xff0c;功能强大&#xff0c;支持调试、代码自动完成、…

Java应用CPU占用过高故障排除

一、背景 最近测试反馈测试环境接口偶现有访问超时&#xff0c;然后APP提示是网络失败&#xff0c;看了一下测试环境的应用完全没啥问题&#xff0c;一直以为是网络问题。 今天测试有反馈了&#xff0c;赶紧看了一下测试服务器&#xff0c;这次终于有症状了&#xff0c;CPU直…

【FPGA项目】沙盘演练——基础版报文收发

​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 第1个虚拟项目 前言 点灯开启了我们的FPGA之路&#xff0c;那么我们来继续沙盘演练。 用一个虚拟项目&#xff0c;来入门练习&#xff0c;以此步入数字逻辑的…

《C++ primer plus》精炼(OOP部分)——对象和类(1)

聪明在于学习&#xff0c;天才在于积累。所谓天才&#xff0c;实际上是依靠学习。 文章目录 概述正文面向对象编程和面向过程编程类和对象类的组成公共接口 类声明访问控制封装 类和结构体类定义 概述 C是门包罗万象的语言&#xff0c;它将各门类的编程思想杂糅&#xff0c;最…

Parallels Desktop 19新功能解析,助力跨平台工作流程提升!

对于许多Mac用户来说&#xff0c;运行Windows应用程序是必不可少的。也许你的雇主使用的软件只适用于Windows&#xff0c;或者需要使用依赖于某些Windows技术的网站。或者你想在Mac上玩Windows游戏。或者&#xff0c;你可能需要在其他操作系统上测试应用程序和服务——你可以在…

系统vcomp120.dll丢失怎么办?要怎样修复呢?三个修复方法分享

我要和大家分享一个关于系统丢失vcomp120.dll文件的问题。这个问题可能会困扰很多使用电脑的朋友&#xff0c;特别是在运行某些软件时&#xff0c;可能会出现“找不到vcomp120.dll”的错误提示。那么&#xff0c;遇到这样的问题&#xff0c;我们应该如何解决呢&#xff1f;接下…

用 ChatGPT 写代码太省时间了

几个月前&#xff0c;我们聊过陶哲轩使用 ChatGPT 辅助解决数学问题。当时&#xff0c;他觉得虽然测试结果不太令人满意&#xff0c;但也并没有对 ChatGPT 持完全否定的态度。他觉得&#xff0c;像 ChatGPT 这类大型语言模型在数学中可以用来做一些半成品的语义搜索工作&#x…

【leetcode 力扣刷题】汇总区间//合并区间//插入区间

一些关于区间的力扣题目 228. 汇总区间56. 合并区间57. 插入区间 228. 汇总区间 题目链接&#xff1a;228.汇总区间 题目内容&#xff1a; 看题目真是没懂这个题到底是要干啥……实际上题目要求的恰好覆盖数组中所有数字的最小有序区间范围列表&#xff0c;这个最小是指一个区…

QT 设置应用程序图标

1.下载xx.ico图标&#xff1a;ico网址 2.在线PNG转换ICO&#xff1a;png在线转换ico 3.添加图标资源 1&#xff09;新建文件路径 2&#xff09;添加图片资源 3&#xff09;在 .pro文件里面添加图片 4&#xff09;将xx.ico放到工程目录&#xff0c;编译完可以看到xx.exe的图标…