UNIXC002 system v IPC、共享内存

system v IPC

在这里插入图片描述

  • system v ipcs 分为以下3种类型
 $ ipcs
# 消息队列
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
# 共享内存
------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 23592960   moonx      700        258104     2          dest         
0x00000000 7340033    moonx      600        1048576    2          dest         
0x00000000 884738     moonx      600        524288     2          dest         
0x00000000 655363     moonx      600        524288     2          dest         
0x00000000 983044     moonx      600        524288     2          dest         
# 信号量级
------ Semaphore Arrays --------
key        semid      owner      perms      nsems     

  • 三种类型的system V IPC都使用IPC键作为它们的标识,IPC键是一个key_t类型的整数,该类型在sys/types.h中定义。
  • IPC键通常是由ftok函数赋予的,该函数把一个已存在的路径名pathname和一个非0整数id组合转换成一个key_t值,即IPC键。

在这里插入图片描述

  • 第一段话的意思就是,shmat 将内核的共享内存段附加到进程地址空间,就算进程结束,内核中的共享内存段依然存在,直到内核重启,或显示删除该对象。

1. ftok 获取system v ipc 的key值

  • 感觉把获取改成赋予更合适。
    在这里插入图片描述
  • IPC键通常是由ftok函数赋予的,该函数把一个已存在的路径名pathname和一个非0整数id组合转换成一个key_t值,即IPC键。
  • 有效指的是,文件存在还能被访问
  • 会采用proj_id的低有效8位,给257(256 + 1)和给1是相同的。给321(256 + 65)和给65是相同的
  • 注意中:两次ftok中,如果两个参数都相同,那么结果值就相同。如果有一个不同,结果值就不同。

2. 代码示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main(int argc, char *argv[]){
    //获取一个system v ipc 的键值
    key_t key = ftok(argv[1], 66);
    // key_t 是整型,可以先gcc -E 得到
    // 以16进制的方式输出
    printf("key:0x%x\n", key);
    return 0;
}
$ ./a.out test 
key:0x42100025
# 这里只是获取到了键值,但是还没有创建system V IPC对象,所以系统中是没有这个键值的,即ipcs查看是没有这个键值的
# 相当于去银行排队存钱,只是拿到了排队号,但是没有办业务,也就不会被银行记录。

深入了解IPC可以参考:信号量

共享内存

在这里插入图片描述

  • 进程1,2分别在用户空间,共享内存在操作系统的内核当中,并被映射到进程1,2当中。这里的内存映射文件是什么?可以想放下,等知识积累到了,就理解了。这篇文章本来就是科普级别的。
  • 但是,如果进程1,2异步去访问这块内存空间,会造成程序的不确定性(bug),可以用信号量级来解决这个问题,这里只是为了了解利用共享内存进行进程间的通信,就先不管这个问题了。

1. shmget 分配一个system v的共享内存段

在这里插入图片描述

  • 第二个参数size, 新建的共享内存大小,以字节为单位
  • IPC_CREAT: 如果共享内存段,直接返回共享内存段的id。如果共享内存段不存在,创建共享内存段,并返回这个id
  • IPC_CREAT|| IPC_EXCL 一起使用时,如果共享内存段存在,就会报错
  • mode是权限
  • 当分配了system v的共享内存段以后,ipcs就能看到对应的键值了。

1.1 代码示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define E_MSG(STRING, VAL) do{perror(STRING); return(VAL);}while(0)

int main(int argc, char *argv[]){
    //获取一个system v ipc 的键值
    key_t key = ftok(argv[1], 66);
    // key_t 是整型,可以先gcc -E 得到
    // 以16进制的方式输出
    printf("key:0x%x\n", key);

    // 使用键值获取共享内存段的id
    int shmid=shmget(key, 1024, IPC_CREAT|IPC_EXCL|0644);
    if(shmid == -1)E_MSG("shmget", -1);
    printf("shmid:%d\n", shmid);
    return 0;
}
$ ./a.out test 
key:0x42100025
shmid:30474264
## 共享内存段已经存在,报错
$ ./a.out test 
key:0x42100025
shmget: File exists
# 这时查看icps就会看到这个键值, 最后的数字是内核中的共享内存段被进程的关联的个数
$ ipcs
...
0x42100025 30474264   moonx      644        1024       0     
...

2. shmat、shmdt 将共享内存段附加(解除)到进程地址空间

  • shmat

  • 进程的地址空间,就是那3G的虚拟地址空间

  • shmaddr 类似于mmap(2)第一个参数的作用,shmflg就设为0(听说基本用不到)
    在这里插入图片描述

  • shmdt

  • shamddr 指的是本进程的共享内存段的具体地址
    在这里插入图片描述

2.1 代码示例:利用共享内存实现进程间的通信

shmA.c 向共享内存段写入数据

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define E_MSG(STRING, VAL) do{perror(STRING); return(VAL);}while(0)

int main(int argc, char *argv[]){
    //获取一个system v ipc 的键值
    key_t key = ftok(argv[1], 66);
    printf("key:0x%x\n", key);

    // 使用键值获取共享内存段的id
    int shmid=shmget(key, 1024, IPC_CREAT|0644);
    if(shmid == -1)E_MSG("shmget", -1);
    printf("shmid:%d\n", shmid);

    // 使用共享内存段的id,将共享内存段关联到进程的地址空间
    void *p=shmat(shmid, NULL, 0);
    if(p==((void*)-1))E_MSG("shamt", -1);
    // 即便这个进程结束了, "hello beijing\n" 这个字符串照样会在内核空间的共享内存里,不会消失
    strcpy(p, "hello beijing\n");
    // 解除关联
    shmdt(p);
    return 0;
}

shmB.c 向共享内存段中读取数据,并输出到屏幕

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define E_MSG(STRING, VAL) do{perror(STRING); return(VAL);}while(0)

int main(int argc, char *argv[]){
    //获取一个system v ipc 的键值
    key_t key = ftok(argv[1], 66);
    printf("key:0x%x\n", key);

    // 使用键值获取共享内存段的id
    int shmid=shmget(key, 1024, IPC_CREAT|0644);
    if(shmid == -1)E_MSG("shmget", -1);
    printf("shmid:%d\n", shmid);

    // 使用共享内存段的id,将共享内存段关联到进程的地址空间
    void *p=shmat(shmid, NULL, 0);
    if(p==((void*)-1))E_MSG("shamt", -1);
    // 把共享内存段中的数据输出到屏幕
    printf("%s", (char*)p);
    // 解除关联
    shmdt(p);
    return 0;
}

$ touch tmp
$ gcc shmB.c -o shmB
$ gcc shmA.c -o shmA
$ shmA tmp
key:0x42082151
shmid:30441487
$ shmB tmp
key:0x42082151
shmid:30441487
hello beijing

3. ipcrm 删除system v对象

man ipcrm
....
       -M, --shmem-key shmkey
              Remove the shared memory segment created with shmkey after the last detach is performed.

       -m, --shmem-id shmid
              Remove the shared memory segment identified by shmid after the last detach is performed.
....
$ ipcrm -m 30441487
$ ipcs #就看不到那个共享内存段了
# 所以也读不到任何东西了
$ $ shmB
key:0xffffffff
shmid:31031311
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值