【IPC通信--共享内存】

这篇具有很好参考价值的文章主要介绍了【IPC通信--共享内存】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

进程间通信目的

数据传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

通信背景

1.由于进程是具有独立性的,进程想交互数据,成本会非常高。但是有些情况下需要多进程处理一件事情。
2.进程独立并不是彻底独立,有时候我们需要双方能够进行一定程度的信息交互。

我们要学的进程间通信,不是告诉我们如何通信,是他们两个如何先看到同一份资源。(文件,内存块…等方式)

共享内存实现进程间通信的原理

共享内存实际是操作系统在实际物理内存中开辟的一段内存。

共享内存实现进程间通信,是操作系统在实际物理内存开辟一块空间,一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间上的共享区的一块地址空间形成映射关系。 ​ 当一个进程往该空间写入内容时,另外一进程访问该空间,会得到写入的值,即实现了进程间的通信。

要实现进程间通信需要两个进程看到同一块空间,系统开辟的共享内存就是两个进程看到的同一资源。

注意:共享内存实现进程间通信是进程间通信最快的。

【IPC通信--共享内存】,进程间通信,linux

如何使⽤

要使⽤⼀块共享内存,进程必须先分配它。其他需要访问这个共享内存块的每⼀个进程都必须将这个共享绑定(attach)到⾃⼰的地址空间中(系统维护⼀个对该内存的引⽤计数器,通过ipcs -s 命令可查看有⼏个进程在使⽤该共享内存块)。当通信完毕后,所有进程从共享内存块脱离,由⼀个进程释放该共享内存块。要注意的是,所有⽤户申请的共享内存块最终⼤⼩都必须是向上取整为系统页⾯⼤⼩的整数倍。在Linux系统中,内存页⾯⼤⼩默认是4KB。

注意:当⼀个进程创建⼀块共享内存后,该进程在主动去释放该共享内存之前,被kill掉时,只会使该进程脱离(detach)该共享内存块,⽽不会释放该共享内存块,这时候可以使⽤命名ipcrm -m 去释放该资源。

相关函数

1、shmget
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);

函数说明:得到⼀个共享内存标识符或创建⼀个共享内存对象并返回共享内存标识符。
参数:
        key: ftok函数返回的I PC键值
        size: ⼤于0的整数,新建的共享内存⼤⼩,以字节为单位,获取已存在的共享内存块标识符时,该参数为0,
        shmflg: IPC_CREAT||IPC_EXCL 执⾏成功,保证返回⼀个新的共享内存标识符,附加参数指定IPC对象存储权限,如|0666
返回值:成功返回共享内存的标识符,出错返回-1,并设置error错误位。

key:共享内存的唯一值,这个参数需要由用户提供。
共享内存要被管理 -> struct shmid_ds -> struct ipc_perm -> key(shmget)(共享内存唯一值)

1. 为什么key值需要由用户提供?
进程间通信的前提是,先让不同的进程,看到同一份资源。如果由操作系统提供,创建共享内存的进程可以知道key值,但是使用共享内存的进程无法获取。所以key值必须由用户获取,然后在使用时标定key值,则能让使用共享内存的进程获取到。

共享内存,在内核中,让不同的进程看到同一份共享内存,做法是:让他们拥有同一个key即可。

匿名管道 --> 约定好使用同一个文件
共享内存 --> 约定好使用同一个唯一key

2.为什么key值要有唯一性?
操作系统中可能有很多个共享内存在被使用,所以我们就需要用一个唯一值来标识每一个共享内存。

3. 那么如何保证key值唯一性呢?
生成唯一key值函数:ftok函数。

key_t ftok(const char *pathname, int proj_id);

将文件路径和一个项目标识符,转化为唯一key值。

返回值:一个整数,创建成功,返回一个合法的共享内存标识符。失败,返回 -1。


2、shmat
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void shmaddr, int shmflg);

函数说明:连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调⽤进程的地址空间
参数:
        shmid: 共享内存标识符
        shmaddr: 指定共享内存出现在进程内存地址的什么位置,通常指定为NULL,让内核⾃⼰选择⼀个合适的地址位置
        shmflg: SHM_RDONLY 为只读模式,其他参数为读写模式
返回值:成功返回附加好的共享内存地址,出错返回-1,并设置error错误位

3、shmdt
#include <sys/types.h>
#include <sys/shm.h>
void *shmdt(const void* shmaddr);

函数说明:与shmat函数相反,是⽤来断开与共享内存附加点的地址,禁⽌本进程访问此⽚共享内存,需要注意的是,该函数并不删除
所指定的共享内存区,⽽是将之前⽤shmat函数连接好的共享内存区脱离⽬前的进程
参数:shmddr 连接共享内存的起始地址
返回值:成功返回0,出错返回-1,并设置error。

4、shmctl
#include <sys/types.h>
#Include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds* buf);

函数说明:控制共享内存块
参数:
        shmid:共享内存标识符
        cmd:
                IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构赋值到buf所指向的buf中
                IPC_SET:改变共享内存的状态,把buf所指向的shmid_ds结构中的uid、gid、mode赋值到共享内存的shmid_ds结构内
                IPC_RMID:删除这块共享内存
        buf:共享内存管理结构体
返回值:成功返回0,出错返回-1,并设置error错误位。

代码演⽰

父子进程通信范例

shm.c源代码如下:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#define SIZE 1024
int main()
{
    int shmid ;
    char *shmaddr ;
    struct shmid_ds buf ;
    int flag = 0 ;
    int pid ;
    shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600 ) ;
    if ( shmid < 0 )
    {
            perror("get shm  ipc_id error") ;
            return -1 ;
    }
    pid = fork() ;
    if ( pid == 0 )
    {
        shmaddr = (char *)shmat( shmid, NULL, 0 ) ;
        if ( (int)shmaddr == -1 )
        {
            perror("shmat addr error") ;
            return -1 ;
        }
        strcpy( shmaddr, "Hi, I am child process!\n") ;
        shmdt( shmaddr ) ;
        return  0;
    } else if ( pid > 0) {
        sleep(3 ) ;
        flag = shmctl( shmid, IPC_STAT, &buf) ;
        if ( flag == -1 )
        {
            perror("shmctl shm error") ;
            return -1 ;
        }
        printf("shm_segsz =%d bytes\n", buf.shm_segsz ) ;
        printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid ) ;
        printf("chlid pid=%d, shm_lpid = %d \n",pid , buf.shm_lpid ) ;
        shmaddr = (char *) shmat(shmid, NULL, 0 ) ;
        if ( (int)shmaddr == -1 )
        {
            perror("shmat addr error") ;
            return -1 ;
        }
        printf("%s", shmaddr) ;
        shmdt( shmaddr ) ;
        shmctl(shmid, IPC_RMID, NULL) ;
    }else{
        perror("fork error") ;
        shmctl(shmid, IPC_RMID, NULL) ;
    }
    return 0 ;
}

编译 gcc shm.c –o shm。
执行 ./shm,执行结果如下:
shm_segsz =1024 bytes
shm_cpid = 9503
shm_lpid = 9504
Hi, I am child process!


⾸先,先来讲⼀下fork之后,发⽣了什么事情。
由fork创建的新进程被称为⼦进程(child process)。该函数被调⽤⼀次,但返回两次。两次返回的区别是⼦进程的返回值是0,⽽⽗进程的返回值则是新进程(⼦进程)的进程 id。将⼦进程id返回给⽗进程的理由是:因为⼀个进程的⼦进程可以多于⼀个,没有⼀个函数使⼀个进程可以获得其所有⼦进程的进程id。对⼦进程来说,之所以fork返回0给它,是因为它随时可以调⽤getpid()来获取⾃⼰的pid;也可以调⽤getppid()来获取⽗进程的id。(进程id 0总是由交换进程使⽤,所以⼀个⼦进程的进程id不可能为0 )。
fork之后,操作系统会复制⼀个与⽗进程完全相同的⼦进程,虽说是⽗⼦关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独⽴的,⼦进程数据空间中的内容是⽗进程的完整拷贝,指令指针也完全相同,⼦进程拥有⽗进程当前运⾏到的位置(两进程的程序计数器pc值相同,也就是说,⼦进程是从fork返回处开始执⾏的),但有⼀点不同,如果fork成功,⼦进程中fork的返回值是0,⽗进程中fork的返回值是⼦进程的进程号,如果fork不成功,⽗进程会返回错误。
可以这样想象,2个进程⼀直同时运⾏,⽽且步调⼀致,在fork之后,他们分别作不同的⼯作,也就是分岔了。这也是fork为什么叫fork的原因,⾄于哪⼀个最先运⾏,可能与操作系统(调度算法)有关,⽽且这个问题在实际应⽤中并不重要,如果需要⽗⼦进程协同,可以通过原语的办法解决。

多进程读写范例

多进程读写即一个进程写共享内存,一个或多个进程读共享内存。下面的例子实现的是一个进程写共享内存,一个进程读共享内存。
(1)下面程序实现了创建共享内存,并写入消息。
shmwrite.c源代码如下:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
typedef struct{
    char name[8];
    int age;
} people;
int main(int argc, char** argv)
{
    int shm_id,i;
    key_t key;
    char temp[8];
    people *p_map;
    char pathname[30] ;
    strcpy(pathname,"/tmp") ;
    key = ftok(pathname,0x03);
    if(key==-1)
    {
        perror("ftok error");
        return -1;
    }
    printf("key=%d\n",key) ;
    shm_id=shmget(key,4096,IPC_CREAT|IPC_EXCL|0600); 
    if(shm_id==-1)
    {
        perror("shmget error");
        return -1;
    }
    printf("shm_id=%d\n", shm_id) ;
    p_map=(people*)shmat(shm_id,NULL,0);
    memset(temp, 0x00, sizeof(temp)) ;
    strcpy(temp,"test") ;
    temp[4]='0';
    for(i = 0;i<3;i++)
    {
        temp[4]+=1;
        strncpy((p_map+i)->name,temp,5);
        (p_map+i)->age=0+i;
    }
    shmdt(p_map) ;
    return 0 ;
}

(2)下面程序实现从共享内存读消息。
shmread.c源代码如下:

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct{
    char name[8];
    int age;
} people;
int main(int argc, char** argv)
{
    int shm_id,i;
    key_t key;
    people *p_map;
    char pathname[30] ;
    strcpy(pathname,"/tmp") ;
    key = ftok(pathname,0x03);
    if(key == -1)
    {
        perror("ftok error");
        return -1;
    }
    printf("key=%d\n", key) ;
    shm_id = shmget(key,0, 0);   
    if(shm_id == -1)
    {
        perror("shmget error");
        return -1;
    }
    printf("shm_id=%d\n", shm_id) ;
    p_map = (people*)shmat(shm_id,NULL,0);
    for(i = 0;i<3;i++)
    {
        printf( "name:%s\n",(*(p_map+i)).name );
        printf( "age %d\n",(*(p_map+i)).age );
    }
    if(shmdt(p_map) == -1)
    {
        perror("detach error");
        return -1;
    }
    return 0 ;
}

(3)编译与执行
①编译gcc shmwrite.c -o  shmwrite。
②执行./shmwrite,执行结果如下:
key=50453281
shm_id=688137
③编译gcc shmread.c -o shmread。
④执行./shmread,执行结果如下:
key=50453281
shm_id=688137
name:test1
age 0
name:test2
age 1
name:test3
age 2
⑤再执行./shmwrite,执行结果如下:
key=50453281
shmget error: File exists
⑥使用ipcrm -m 688137删除此共享内存。
 文章来源地址https://www.toymoban.com/news/detail-792887.html

到了这里,关于【IPC通信--共享内存】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】进程间通信之共享内存

    共享内存比管道快哦~ 文章目录 前言 一、共享内存的实现原理 二、实现共享内存的代码 总结 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的

    2024年02月03日
    浏览(51)
  • Linux 共享内存mmap,进程通信

    进程间通信是操作系统中重要的概念之一,使得不同的进程可以相互交换数据和进行协作。其中,共享内存是一种高效的进程间通信机制,而内存映射(mmap)是实现共享内存的一种常见方法。 存储映射 I/O 是 一个磁盘文件 与 存储空间中的一个缓冲区相映射 。于是, 当从缓

    2024年02月13日
    浏览(45)
  • 【Linux】进程间通信——管道/共享内存

    进程间通信( Inter-Process Communication,简称IPC )是指不同进程之间进行数据交换和共享信息的机制和技术。在操作系统中,每个进程都是独立运行的,有自己的地址空间和数据,因此进程之间需要一种机制来进行通信,以便彼此协调工作、共享数据或者进行同步操作。 进程间

    2024年02月16日
    浏览(45)
  • 进程间通信--共享内存详解【Linux】

    本文详细讲解了共享内存的原理和使用,并且通过实例代码角度来深度理解共享内存,下面就让我们开始吧。 数据传输:一个进程需要将它的数据发送给另一个进程 资源共享:多个进程之间共享同样的资源。 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(

    2024年02月02日
    浏览(43)
  • 【hello Linux】进程间通信——共享内存

    目录 前言: 1. System V共享内存 1. 共享内存的理解 2. 共享内存的使用步骤 3. 共享内存的使用         1. 共享内存的创建         查看共享内存         2. 共享内存的释放         3. 共享内存的挂接         4. 共享内存的去挂接 4. 共享内存的使用示例 1. 两进

    2024年02月01日
    浏览(106)
  • Linux--进程间的通信-共享内存

    前文: Linux–进程间的通信-匿名管道 Linux–进程间的通信–进程池 Linux–进程间的通信-命名管道 对于两个进程,通过在内存开辟一块空间(操作系统开辟的),进程的虚拟地址通过页表映射到对应的共享内存空间中,进而实现通信 ; 特点和作用: 高效性: 共享内存是一种

    2024年04月26日
    浏览(43)
  • system-v IPC共享内存通信

    目录 system v IPC简介 共享内存需要用到的函数接口 shmget函数--获取对象ID shmat函数--获得映射空间 shmctl函数--释放资源 共享内存实现思路 注意 消息队列、共享内存和信号量统称为system v IPC(进程间通信机制),V是罗马数字5,是UNIX的ATT分支的其中一个版本,一般称它们为IPC对象

    2024年04月26日
    浏览(50)
  • 【Linux】进程间的通信之共享内存

    利用 内存共享 进行进程间的通信的原理其实分为以下几个步骤: 在物理内存中创建一块共享内存。 将共享内存链接到要通信的进程的页表中,并通过页表进行进程地址空间的映射。 进程地址空间映射完毕以后返回首个虚拟地址,以便于进程之间进行通信。 根据共享内存的

    2024年02月09日
    浏览(52)
  • 【Linux】进程间通信 -- system V共享内存

    共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据 理解: 进程间通信,是专门设计的,用来IPC 共享内存是一种通信方式,所有想通信的进程

    2024年02月16日
    浏览(50)
  • 【Linux】进程间通信——system V共享内存

    目录  写在前面的话 System V共享内存原理 System V共享内存的建立 代码实现System V共享内存 创建共享内存shmget() ftok() 删除共享内存shmctl() 挂接共享内存shmat() 取消挂接共享内存shmdt() 整体通信流程的实现          上一章我们讲了进程间通信的第一种方式 --- 管道,这一章我

    2024年02月14日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包