Linux进程间通信学习记录(IPC 机制以及共享内存)

news2024/11/15 23:28:28

0.System V IPC机制:

        ①.IPC对象包含:共享内存、消息队列和信号灯集。

        ②.每个IPC对象有唯一的ID。

        ③.IPC对象创建后一直存在,直到被显示地删除。

        ④.每一个IPC对象有一个关联的KEY。(其他进程通过KEY访问对应的IPC对象)

        ⑤.ipcs(显示系统所有的IPC对象)、ipcrm

一. 生成KEY值 - ftok

功能

        成功时返回(合法的key值),失败时返回(EOF)。

        若传入的参数一样,那么得到的key值也一样

参数

        path:存在且可访问的文件的路径

        proj_id:用于生成key的数字,不能为0,一般为字符常量

int key_t ftok(const char *path,int proj_id);
int main(int argc,char *argv[])
{
    key_t key;

    key = ftok(".",'a');
    if(-1 == key)
    {
        perror("key error");
        exit(-1);
    }
}

二.共享内存

0.特点

        ①.共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。

        ②.共享内存在内核空间创建,可被进程映射到用户空间访问,使其灵活。

        ③.由于多个进程可同时访问共享内存,因此需要同步和互斥机制配合使用

        ④.共享内存的大小有限制

1.创建/打开共享内存 - shmget()

功能

        创建/打开共享内存对象

        成功时返回(共享内存的id),失败时(EOF)

参数

        key:和共享内存关联的key,IPC_PRIVATE或由ftok生成。

        size:共享内存的大小,单位为字节。

        shmflg:共享内存标志位 IPC_CREATE | 0666(不存在就创建,存在了就打开)。

int shmget(key_t key,int size,int shmflg);

示例1:创建一个私有的共享内存,大小为512字节,权限为0666

int shmid;

shmid = shmget(IPC_PRIVATE,512,0666);
if(0 > shmid)
{
    perror("shmget error");
    exit(-1);
}

示例2:创建/打开一个和key关联的共享内存,大小为1024字节,权限为0666

key_t key;
int shmid;

/* 1.先生成key(相关的进程调用ftok时,传入相同的参数才能得到相同的key) */
key = ftok(".",'m');
if(-1 == key)
{
    perror("ftok");
    exit(-1);
}

/* 2.获取共享内存 */
shmid = shmget(key,1024,IPC_CREATE | 0666);
if(0 > shmid)
{
    perror("shmget");
    exit(-1);
}

2.映射共享内存 - shmat

功能

        映射共享内存;

        成功时返回(映射后的地址),失败时返回((void *)-1),通过指针访问共享内存,指针类型取决于共享内存中存放的数据的类型;

参数

        shmid:要映射的共享内存的id。

        shmaddr:指定映射后的地址(通常传入NULL),NULL表示由系统自动映射。

        shmflg:标志位,0表示可读写;SHM_RDONLY表示只读。

void *shmat(int shmid,const void *shmaddr,int shmflg);

示例1:在共享内存中存放键盘输入的字符串

char *addr;    //保存映射后的首地址
int shmid;

/* 映射共享内存 */
addr = (char *)shmat(shmid,NULL,0);
id((char *)-1 == addr)
{
    perror("shmat");
    exit(-1);
}
fgets(addr,N,stdin);

3.读写共享内存

        操作映射的共享内存的地址

4.撤销共享内存映射 - shmdt

功能

        撤销共享内存映射

        成功时返回(0),失败时返回(EOF)

参数

        shmaddr:映射共享内存的首地址

int shmdt(void *shmaddr);

5.共享内存控制 - shmctl

功能

        控制共享内存。

        成功时返回(0),失败时返回(-1)并设置errno。

参数

        shmid:要操作的共享内存的id。

        cmd   :要执行的操作

                        IPC_STAT:获取当前共享内存的属性(大小,关联的key值,权限等),存入到                                                shmid_ds *buf中

                        IPC_SET  :将属性(先在shmid_ds *buf中填充)设置到共享内存的id中

                        IPC_RMID:删除一个共享内存的id

        buf:保存或设置共享内存属性的地址

int shmctl(int shmid,int cmd,struct shmid_ds *buf);

6.删除共享内存对象 - shmctl

 用shmctl来删除共享内存对象(应该由最后一个进程来执行此操作)

 功能

        添加删除标记,当每一个进程都撤销共享内存映射后(nattach == 0)才真正的删除。        

shmctl(shmid,IPC_RMID,NULL);

三.信号灯

1.含义:

        信号灯也叫信号量,用于进程/线程同步或互斥的机制;

        System V 信号灯是一个或多个计数信号灯的集合;

        System V 信号灯可同时操作集合中的多个信号灯,要么都能申请到资源,要么都申请不到资          源;

        System V 信号灯申请多个资源时避免死锁;

 2.信号灯的类型:

         Posix无名信号灯:通常用于线程同步

         Posix有名信号灯:通常用于进程间的同步和互斥

         System V 信号灯:通常用于进程间的同步和互斥

四.System V 信号灯使用步骤

1.打开/创建信号灯 - semget

功能

        打开/创建信号灯

        成功时返回(信号灯集合的id),失败时返回(-1)

参数

        key:和信号灯关联的key值(IPC_PRIVATE 或 ftok生成的);

        nsems:集合中包含的计数信号灯的个数;

        semflg:标志位

                        IPC_CREAT | 0666:没有则创建;

                        IPC_EXCL:检查信号灯集合是不是第一次被创建,若不是第一次被创建则会返回                                             (EEXIST),保证信号灯只被初始化一次;

int semget(key_t key,int nsems,int semflg);

2.信号灯初始化 - semctl

功能

        初始化信号灯集合;

        成功时返回(0),失败时返回(EOF);

参数

        semid:要操作的信号灯集合id

        semnum:要操作的集合中的信号灯编号(信号灯集合中的哪一个信号灯)

        cmd:要执行的操作

                        SETVAL:设置信号灯的值

                        IPC_RMID:删除信号灯集合

        union semun  :要设置的信号灯的值

int semctl(int semid,int semnum,int cmd,...);
union semun myun;

/* 1.填充第一个要初始化的信号的值 */
myun.val = 2;
/* 把第一个信号灯的值初始化为2 */
if(0 > semctl(semid,0,SETVAL,myun))
{
    perror("semctl");
    exit(-1);
}

/* 1.填充第二个要初始化的信号的值 */
myun.val = 0;
/* 把第二个信号灯的值初始化为0 */
if(0 > semctl(semid,1,SETVAL,myun))
{
    perror("semctl");
    exit(-1);
}

3.P/V 操作 - semop

功能

        对信号灯集合中的一个或多个信号灯执行操作;

        成功时返回(0),失败时返回(-1);

参数

        semid:要操作的信号灯集id;

        sops:描述对某一个信号灯操作的结构体。若要操作多个信号灯,则要传入结构体数组;

        nsops:要操作的信号灯的个数;

int semop(int semid,struct sembuf *sops,unsigned nsops);
struct sembuf
{
    short semnum;    //指定要操作的信号灯的编号

    /* 指定要执行的操作(-1:P操作,1:V操作) */
    short sem_op;    

    /* 一般为 0/IPC_NOWAIT (0         :表示以阻塞的方式,直到完成操作
                            IPC_NOWAIT : 表示不阻塞,直接返回) */
    short sem_flg;    
};
    struct sembuf buf[3];

    /* 填充第 0 个信号 */
    buf[0].sem_num = 0;     //操作第 0 个信号灯
    buf[0].sem_op = -1;     //指定执行 P 操作
    buf.sem_flg = 0;        //以阻塞的方式完成操作

    /* 填充第 1 个信号 */
    buf[1].sem_num = 1;     //操作第 1 个信号灯
    buf[1].sem_op = -1;     //指定执行 P 操作
    buf[1].sem_flg = 0;     //以阻塞的方式完成操作


    /* 调用 semop 完成P/V操作,操作的信号灯的个数为 2  */
    semop(semid,buf,2);

3.删除信号灯 - semctl

功能

        删除信号灯集合;

        成功时返回(0),失败时返回(EOF);

参数

        semid:要操作的信号灯集合id

        semnum:要操作的集合中的信号灯编号(信号灯集合中的哪一个信号灯)

        cmd:要执行的操作

                        SETVAL:设置信号灯的值

                        IPC_RMID:删除信号灯集合

int semctl(int semid,int semnum,int cmd,...);
semctl(semid,0,IPC_RMID);

五.用信号灯集实现共享内存同步

1.目的:

        父子进程通过System V 信号灯同步对共享内存的读写,需要创建两个信号灯,一个用于写资源,一个用于读资源

        父进程从键盘输入字符串到共享内存

        子进程删除字符串中的空格并打印

        父进程输入 quit 后,删除共享内存和信号灯集,程序结束

2.流程图

3.代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

void init_sem(int semid , int s[] , int n);
void pv(int semid,int num,int op);

#define N   64      //共享内存的大小
#define READ    0   //信号灯集合中的信号灯的编号,代表可读缓冲区
#define WRITE   1   //信号灯集合中的信号灯的编号,代表可写缓冲区

/* 用于信号灯集合操作, man 手册里有这个共用体,但是得自己定义 */
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};


int main(void)
{
    /**
     * 0.定义所要用到的变量
     * (1)共享内存的id
     * (2)信号灯集合id
     * (3)信号灯初始化数组
     * (4)pid
     * (5)key
     * (6)共享内存映射后的首地址
    */
    int shmid;          //共享内存的id
    int semid;          //信号灯集合id
    int s[] = {0,1};    //信号灯初始化数组,s[0]用于读,s[1]用于写
    pid_t pid;          //用于创建进程
    key_t key;          //用于生成key
    char *shmaddr;      //用于接收映射的共享内存的首地址

    /* 1.创建/获取key */
    key = ftok(".",'s');
    if(-1 == key)
    {
        perror("ftok error");
        exit(-1);
    }

    /* 2.创建/获取共享内存 */
    shmid = shmget(key,N,IPC_CREAT | 0666);
    if(0 > shmid)
    {
        perror("shmget error");
        exit(-1);
    }

    /* 3.创建信号灯集合(若失败则在退出程序前,要先把创建号的共享内存删除) */
    semid = semget(key,2,IPC_CREAT | 0666);
    if(0 > semid)
    {
        perror("semget error");
        goto _error1;       //处理错误
    }

    /* 4.对信号灯集合进行初始化,初始化 2 个信号量 */
    init_sem(semid,s,2);

    /* 5.映射共享内存(64)个字节,(若映射共享内存失败,在退出程序前,要先把之前创建的信号灯集合删除) */
    shmaddr = (char *)shmat(shmid,NULL,0);
    if(((void *)-1) == shmaddr)
    {
        perror("shmat error");
        goto _error2;
    }

    /* 6.创建子进程 */
    pid = fork();
    switch(pid)
    {
        case -1:
            perror("fork error");
            goto _error2;
            break;
        /* 子进程 */
        case 0:
        {
            char *p,*q;
            while(1)
            {
                /* 1.对可读资源进行 P 操作 */
                pv(semid,READ,-1);

                /* 2.进行读操作(去除共享内存中的空格) */
                p = q = shmaddr;
                while(*q)
                {
                    if(*q != ' ')
                    {
                        *p++ = *q;
                    }
                    q++;
                }
                *p = '\0';
                printf("result:%s\n",shmaddr);

                /* 3.对可写资源执行 V 操作,使其他进程可写 */
                pv(semid,WRITE,1);
            }
            break;
        }

        /* 此进程 */
        default:
            while(1)
            {
                /* 7.要写缓冲区,所以先进行P操作,看有没有资源可写。若没有资源可写,则阻塞等待 */
                pv(semid,WRITE,-1);
                
                /* 8.执行写操作 */
                printf("input >\n");
                fgets(shmaddr,N,stdin);
                
                /* 9.若输入了quit,则跳出循环 */
                if(0 == strcmp(shmaddr,"quit\n"))
                    break;
                
                /* 10.执行完写操作后,执行 V 操作,使其他进程可读 */
                pv(semid,READ,1);
            }
            /* 发出信号,结束子进程 */
            kill(pid,SIGUSR1);
            break;
    }


/* 若创建信号灯集合失败,则在退出程序前,要先把创建号的共享内存删除 */
_error1:
    shmctl(shmid,IPC_RMID,NULL);

/* 若映射共享内存失败,在退出程序前,要先把之前创建的信号灯集合删除 */
_error2:
    semctl(semid,0,IPC_RMID);


    return 0;
}


/**
 * @description:    初始化信号灯集合
 * @param - semid:  信号灯集合的id
 * @param - s[]  :  信号灯初始化值的数组
 * @param - n    :  初始化信号灯的个数  
 * @return       :  无 
*/
void init_sem(int semid , int s[] , int n)
{
    int i;
    union semun myun;       //创建信号灯集合共用体

    /* 循环完成信号灯集合的初始化 */
    for(i = 0;i < n;i++)
    {
        myun.val = s[i];
        semctl(semid,i,SETVAL,myun);
    }


}


/**
 * @description:        对信号灯集合的某一个信号灯实现P/V操作
 * @param - semid   :   信号灯集合id
 * @param - num     :   信号灯集合内的信号灯的编号
 * @param - op      :   实现的操作
 * @return          :   无
*/
void pv(int semid,int num,int op)
{
    struct sembuf buf;

    buf.sem_num = num;      //指定操作编号为 num 的信号灯
    buf.sem_op = op;        //指定要执行的操作
    buf.sem_flg = 0;        //阻塞等待,直到该操作完成

    /* 进行P/V操作 */
    semop(semid,&buf,1);
}

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

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

相关文章

索引的设计原则

4.索引的设计原则 4.1.数据准备 a.创建数据库、创建表 CREATE DATABASE atguigudb1; USE atguigudb1;# 创建学生表和课程表 CREATE TABLE student_info (id INT(11) AUTO_INCREMENT,student_id INT NOT NULL ,name VARCHAR(20) DEFAULT NULL,course_id INT NOT NULL ,class_…

x64汇编语言与逆向工程实战指南(四)

目录 1. 数组的存储与遍历1.1 编写数组数据写入内存1.2 汇编循环遍历数组元素 3. Lea指令4. mul指令与imul指令4.1 mul 指令4.2 imul 指令总结 5. div指令5.1 div 指令的基本原理5.2 8 位除法5.3 16 位除法5.3 32 位除法特点和要求 1. 数组的存储与遍历 1.1 编写数组数据写入内…

基于Web的可回收物品收购系统-计算机毕业设计源码49082

摘 要 随着电子商务和在线交易的快速发展&#xff0c;二手商品市场逐渐成为一个不可忽视的经济领域。其中&#xff0c;可回收系统的收购成为了一个关键环节&#xff0c;它不仅有助于资源的有效再利用&#xff0c;还对环境保护和可持续发展起到了积极的推动作用。Servlet可回收…

单片机中的存储器讲解

单片机中的存储器 目录 单片机中的存储器常用的存储器易失性存储器RAMSRAMDRAM 非易失性存储器ROMMask ROMPROMEPROME2PROMFlashNOR FlashNADN Flash 单片机里全局变量、局部变量、堆、栈的存储区域区域介绍栈区堆区静态区代码区常量区 内存分区分类四个区域 常用的存储器 易失…

【Linux】:实现一个简易的shell

目录 1.命令行提示符 2.命令行参数 2.1 获取命令行参数 2.2 解析命令行参数 3.判断指令类型 3.1 模拟cd命令 3.2 模拟export和echo bash的环境变量来源 4.外部指令的执行 1.命令行提示符 在我们输入指令前&#xff0c;终端界面一般有一个命令行提示符&#xff0c; …

性能优化理论篇 | swap area是个什么东西

我们知道每台计算机的内存&#xff08;RAM&#xff09;都是有限的&#xff0c;而我们的应用程序需要加载到内存才能被运行&#xff0c;如果一台机器运行多个应用程序时&#xff0c;内存可能会耗尽。Linux 系统中的“交换空间&#xff08;也称为交换分区&#xff09;”可以帮助缓…

VM相关配置及docker

NAT——VMnet8网卡 桥接——WLAN/网线 仅主机——VMnet1网卡 docker与虚拟机的区别 启动docker服务 systemctl start docker 重启 systemctl start docker关闭docker服务 systemctl stop docker.servicedocker的两大概念 镜像&#xff1a;images&#xff0c;应用程序的静态文…

nssctf-[SWPUCTF 2022 新生赛]1z_unserialize-简单的序列化题目

1. 打开题目是一段很简单的php代码 对代码进行分析&#xff0c;题目中有一个__destruct析构函数&#xff0c;这个函数是在对象被销毁的时候触发&#xff0c;那那么在这里new一下就相当于销毁一个对象&#xff0c;$a$this->lt;和$a($this->lly);相当于是$this->lt(thi…

CSS3-新特性

1.新增选择器 1.属性选择器 2.结构伪类选择器 3.伪元素选择器&#xff08;重点&#xff09; 4.CSS3 盒子模型 2.CSS3滤镜filter 3.CSS3 calc 函数 4.CSS3 过渡&#xff08;重点&#xff09;

8月18日微语报,星期日,农历七月十五

8月18日微语报&#xff0c;星期日&#xff0c;农历七月十五&#xff0c;周末愉快&#xff01; 一份微语报&#xff0c;众览天下事&#xff01; 1、南昌从业者谈蔬菜涨价&#xff1a;天热易变质增加损耗&#xff0c;农户收入未明显提升。 2、委员建议“行政教学分离”&#x…

后端开发刷题 | 寻找峰值【二分法】

描述 给定一个长度为n的数组nums&#xff0c;请你找到峰值并返回其索引。数组可能包含多个峰值&#xff0c;在这种情况下&#xff0c;返回任何一个所在位置即可。 1.峰值元素是指其值严格大于左右相邻值的元素。严格大于即不能有等于 2.假设 nums[-1] nums[n] −∞ 3.对于…

【精选】基于Java摄影约拍系统设计与实现(全网独一无二,最新定制)

目录 目录&#xff1a; 系统简介&#xff1a; 核心技术介绍 mysql技术介绍 IDEA编译器介绍 Springboot框架简介 springmvc框架简介 Mybatis技术简介 Node.js技术简介 Vue.js技术简介 系统数据库详细设计 系统功能设计 系统测试运行 模块测试 系统整体测试 测试过程 测试…

启明欣欣STM32开发板运行ThreadX

ThreadX是非常优秀的RTOS&#xff0c;微软收购了ThreadX后就开源了&#xff0c;后来又交给Eclipse基金会&#xff0c; 本文讲述如何在STM32上运行ThreadX&#xff0c;使用CubeMX来实现。本人环境如下&#xff0c; CM4芯片&#xff1a;STM32F407ZGT6&#xff0c;内存192KB&am…

分布式事务方案——基于两阶段提交的 XA事务

分布式事务方案——基于两阶段提交的 XA事务 在这篇文章中深入理解分布式事务中的两阶段提交&#xff08;2PC&#xff09;&#xff0c;什么是2PC&#xff0c;2PC原理是怎样&#xff1f;2PC有没有什么问题&#xff1f;解决方案&#xff1f;无法解决的情况&#xff1f; 我们详细…

24/8/18算法笔记 目标导向强化学习

目标导向强化学习&#xff08;Goal-Oriented Reinforcement Learning&#xff0c;简称GORL&#xff09;是强化学习的一个分支&#xff0c;它关注于智能体如何通过与环境的交互来实现特定的目标或任务。与传统的强化学习不同&#xff0c;目标导向强化学习更加关注目标的设定和达…

图像数据处理13

三、空域滤波 3.1滤波器的基本概念 什么是滤波&#xff1f; 简单来说就是从干扰信号中提取出有用的信号 3.1.1空域滤波&#xff08;Spatial Domain Filtering&#xff09; 空域滤波适用于简单的滤波任务&#xff0c;直接对图像的像素空间进行操作。它通过对图像中的每个像…

如何选择流量与商业潜力兼备的SEO关键词?

如何选择流量与商业潜力兼备的SEO关键词&#xff1f; 你选择的关键词可以成就或破坏你的SEO活动。 如果你明智地选择关键词&#xff0c;那么你制作的内容将有可能月复一月地吸引有价值的自然搜索流量。如果你选择了错误的关键词&#xff0c;你的内容将只能吸引低价值的流量&a…

Java流程控制07:增强for循环

本节内容视频链接&#xff1a;Java流程控制10&#xff1a;增强for循环_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV12J41137hu?p42&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 Java中的增强for循环&#xff08;‌也称为“for-each”循环&#xff09;‌是…

实用好软-----电脑端好用的免费音乐下载小工具 简单 快速

目前很多很多音乐软件都是收费的。要么是试听。前段时间分享了一款嗅探工具感觉很不错。今天分享的这款小工具超小。下载只有3M大小。解压后运行窗口简单。直接输入歌曲名字即可搜索出来。然后选择下载即可 界面 测试了下还是比较好用的。而且下载很快 &#xff0c;不过软件显…

Ajax-02.Axios

Axios入门 1.引入Axios的js文件 <script src"js/axios-0.18.0.js"></script> Axios 请求方式别名: axios.get(url[,config]) axios.delete(url[,config]) axios.post(url[,data[,config]]) axios.put(url[,data[,config]]) 发送GET/POST请求 axios.get…