Linux加油站 - 进程通信(3)

目录

共享内存

IPC操作命令

总结

共享内存与内存映射区别


共享内存

        共享内存允许两个或者多个进程共享物理内存的同一块区域 (通常被称为段) 。由于一个共享内存段会称为一个进程用户空间的一部分,需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用。

        共享内存通信的原理就是拿出⼀块虚拟地址空间来,映射到相同的物理内存中。进程写入的东西,另外⼀个进程马上就能看到,不需要经过拷贝,大大提高了进程间通信的效率:

        共享内存接口:

int shmget(key_t key, size_t size, int shmflg);    //功能:创建一个新的共享内存段,或者获取一个既有的共享内存段的标识。新创建的内存段中的数据都会被初始化为0
    - 参数:
        - key : key_t类型是一个整形,通过这个找到或者创建一个共享内存。一般使用16进制表示,非0值
        - size: 共享内存的大小
        - shmflg: 属性
            - 访问权限
            - 附加属性:创建 / 判断共享内存是不是存在
                - 创建:IPC_CREAT
                - 判断共享内存是否存在: IPC_EXCL , 需要和IPC_CREAT一起使用
                    IPC_CREAT | IPC_EXCL | 0664
        - 返回值:
            失败:-1 并设置错误号
            成功:>0 返回共享内存的引用的ID,后面操作共享内存都是通过这个值。

void *shmat(int shmid, const void *shmaddr, int shmflg);    //和当前的进程进行关联
    - 参数:
        - shmid : 共享内存的标识(ID),由shmget()返回值获取
        - shmaddr: 申请的共享内存的起始地址,指定NULL,内核指定
        - shmflg : 对共享内存的操作
            - 读 : SHM_RDONLY, 必须要有读权限
            - 读写: 0
    - 返回值:
        成功:返回共享内存的首(起始)地址。  失败: (void *) -1

int shmdt(const void *shmaddr);        //解除当前进程和共享内存的关联
    - 参数:
        shmaddr:共享内存的首地址
    - 返回值:成功 0, 失败 -1

int shmctl(int shmid, int cmd, struct shmid_ds *buf);  //对共享内存进行操作。删除共享内存,共享内存要删除才会消失,创建共享内存的进程被销毁了对共享内存是没有任何影响。
    - 参数:
        - shmid: 共享内存的ID
        - cmd : 要做的操作
            - IPC_STAT : 获取共享内存的当前的状态
            - IPC_SET : 设置共享内存的状态
            - IPC_RMID: 标记共享内存被销毁
        - buf:需要设置或者获取的共享内存的属性信息(对应cmd参数属性)
            - IPC_STAT : buf存储数据
            - IPC_SET : buf中需要初始化数据,设置到内核中
            - IPC_RMID : 没有用,NULL

key_t ftok(const char *pathname, int proj_id);    //根据指定的路径名,和int值,生成一个共享内存的key
    - 参数:
        - pathname:指定一个存在的路径
            /home/Linux/a.txt
        - proj_id: int类型的值,但是这系统调用只会使用其中的1个字节
                   范围 : 0-255  一般指定一个字符 'a'

       

         定义两个文件 write.c , read.c 代表两个进程,write.c文件里创建一个共享内存,write.c往共享内存里写数据, read.c获取共享内存,并从共享内存里读数据:

        write.c :

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main() {    
    int shmid = shmget(100 , 4096 , IPC_CREAT | 0664);     // 创建一个共享内存

    void *ptr = shmat(shmid ,NULL , 0);  // 和当前进程进行关联,调用attach会返回共享内存的首地址
    //  写数据
    char * str = "hello, i am process one !!! ";
    memcpy(ptr ,str , strlen(str)+1 );
    printf("按任意键继续\n");
    getchar();               //让程序停在这

    shmdt(ptr);     // detach解除关联
    
    shmctl(shmid , IPC_RMID , NULL);  // 删除共享内存   

    return 0;
}

        read.c

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main() {    
    int shmid = shmget(100 , 0 ,IPC_CREAT);    // 获取定义好的共享内存

    void *ptr = shmat(shmid , NULL  , 0);    //和当前进程进行关联

    // 读数据
    printf("%s\n" , (char*) ptr);
    
    printf("按任意键继续\n");    //暂停一下
    getchar();

    shmdt(ptr);      // 解除关联

    shmctl(shmid, IPC_RMID, NULL);    // 删除共享内存

    return 0;
}

        编译运行 write.c :

         新建终端编译运行 read.c :

         可以看到简单的完成了两个进程间的通信。

IPC操作命令

        linux上可以直接通过 ipcs命令来操作有关于进程通信的各种信息:

ipcs-a     //打印当前系统中所有的进程间通信方式的信息
ipcs -m    // 打印出使用共享内存进行进程间通信的信息
ipcs -q    // 打印出使用消息队列进行进程间通信的信息
ipcs -s    //打印出使用信号进行进程间通信的信息

       再次编译运行上面的 write.c 文件,然后输入 ipcs -m :

         可以看到 key:0x00000064(16进制),其实就是我们一开始shmget( )函数的第一个参数 10(二进制) ,key用来在系统中唯一标识一块共享内存,和进程的PID类似。

        nattch 代表当前与这个共享内存关联的进程数, 1 也就是指的是 write.c这个进程。编译运行read.c 再次查看: 

         当我们终止掉 write.c 进程时:

         可以看到key值变为了0 ,代表的是当前共享内存处于标记删除状态 ,因为还有read.c与之关联。

总结

        1 .对于一个共享内存,实现采用的是引用计数的原理,当进程脱离共享存储区后,计数器减1,挂架成功时,计数器加1,只有当计数器变为零时,才能被删除。当进程终止时,它所附加的共享存储区都会自动脱离。

        2. 所有进程操作的是同一块共享内存,因为直接在内存上操作,所以共享内存的速度也提高。

共享内存与内存映射区别

        1. 内存映射是在磁盘上建立一个文件,每个进程地址空间中都会开辟出一块空间进行 文件-内存的映射 ;共享内存每个进程最终会映射到同一块物理内存。共享内存保存在物理内存,这样读写的速度要比磁盘要快,但是存储量不是特别大。(mmap每个进程都会有自己的内存映射区,shm是映射到同一物理内存)

        2. 共享内存可以直接创建,内存映射需要磁盘文件(匿名映射除外,但其只能用在有血缘关系的进程间)       

         3. 当机器宕机,因为内存映射把文件保存在磁盘上,这个文件还保存了操作系统同步的映像,所以内存映射不会丢失,但是共享内存就会丢失。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值