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