1.1 共享内存
1.1.1 数据结构
struct shmid_ds {
struct ipc_perm shm_perm; /* 超作许可权数据结构指针 */
int shm_segsz; /* 共享存储段大小 (bytes) */
time_t shm_atime; /* 最后调用shmat时间 */
time_t shm_dtime; /* 最后调用shmdt的时间 */
time_t shm_ctime; /* 最后调用shmctl的改变的时间 */
unsigned short shm_cpid; /*创建者的进程ID */
unsigned short shm_lpid; /* 最后对共享存储段进行操作的进程ID */
short shm_nattch; /* 当前连接数 */
};
struct ipc_perm {
key_t key;
ushort uid; /* owner euid and egid */
ushort gid;
ushort cuid; /* creator euid and egid */
ushort cgid;
ushort mode; /* lower 9 bits of shmflg */
ushort seq; /* sequence number */
};
1.1.2 API
4.4 #include <sys/ipc.h> #include <sys/shm.h> |
打开创建存储段 |
int shmget(key_t key, size_t size, int shmflg); |
返回:失败-1, 成功返回非负的共享存储段id |
第一个参数key是共享存储关键字。它有特殊值IPC_PRIVATE表示总是创建一个进程私有的共享存储段。
当key值不等于IPC_PRIVATE时,shmget动作取决于最后一个参数shmflg标志:
1. IPC_CREAT 单独设置此标志,当系统中不存在相同key时,创建一个新的共享存储段,否则返回已存在的共享存储段。
2. IPC_EXCL 单独设置不起作用。与 IPC_CREAT同时设置时,当系统中存在相同key时,错误返回。保证不会打开一个已存在的共享存储段。
如果没有指定IPC_CREATE并且系统中不存在相同key值的共享存储段,将失败返回。
第三个参数也可以设置共享存储段的访问权限,用或于上面的值操作。
第二个参数size指明要求的共享存储段的大小。当key指定的共享存储段已存在时,取值范围为0和已存在共享段大小。或简单指定为0。
成功后此函数返回共享存储段id,同时创建于参数key相连的数据结构shmid_ds。此节构为系统内部使用。
4.4 #include <sys/ipc.h> #include <sys/shm.h> |
存储段控制函数。可获得shmid_ds全部内容 |
int shmctl(int shmid, int cmd, struct shmid_ds *buf); |
返回:失败-1并置errno, 成功返回0 |
第一个参数,必须由shmget返回的存储段的id。cmd为指定要求的操作。
CMD | 说明 | 参数 |
IPC_STAT | 放置与shmid相连的shmid_ds结构当前值于buf所指定用户区 | buf |
IPC_SET | 用buf指定的结构值代替与shmid相连的shmid_ds结构值 | buf |
IPC_RMID | 删除制定的信号量集合 |
|
SHM_LOCK | 锁住共享存储段。只能由超级管理员使用 |
|
SHM_UNLOCK | unlock共享存储段。只能由超级管理员使用 |
|
//创建/打开共享存储段,连接他到用户地址空间,返回他在用户空间的地址
void* shminit(key_t key, char* mode_str, int target=1) {
int shmid;
int mode;
void* retval;
if (mode_str == NULL) {
mode = 0660;
} else {
sscanf(mode_str, "%o", &mode);
}
if (target == 0 ) {
if ((shmid = shmget(key, sizeof(exchange_T), mode | IPC_CREAT)) == -1)
perror("cannot create shm--");
} else if (target == 1) {
if ((shmid = shmget(key, sizeof(exchange_T), mode)) == -1)
perror("cannot get shm--");
}
if ((retval = shmat(shmid, (void *)0, 0)) == (void*)-1)
perror("shmmat");
return retval;
}
int rm_shm(key_t key) {
int status, shmid;
if ((shmid = shmget(key, sizeof(exchange_T), 0x660 | IPC_CREAT)) < 0)
perror("cannot get shm");
if ((status = shmctl(shmid, IPC_RMID, NULL)) < 0)
perror("shmctl IPC_RMID");
return status;
}
4.5 #include <sys/types.h> #include <sys/shm.h> |
存储段连接 |
void *shmat(int shmid, const void *shmaddr, int shmflg); |
返回:失败-1并置errno, 成功返回连接的实际地址 |
第一个参数,必须由shmget返回的存储段的id
第二个参数指明共享存储段要连接到的地址。0,系统为我们创建一个适当的地址值。否则自己指定
第三个参数shmflg可以设成:SHM_RND, SHM_RDONLY。SHM_RND为自己指定连接地址时用。SHM_RDONLY说明此存储段只读。
4.6 #include <sys/types.h> #include <sys/shm.h> |
存储段分离 |
int shmdt(const void * shmaddr); |
返回:失败-1并置errno, 成功返回0 |
分离由shmaddr指定的存储段。此值应该由shmat返回的值。
此函数不是删除共享存储段,而是从当前进程分离存储段。
当进程推出时,系统会自动分离它连接的所有共享段。
1.1.3 Example
client.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <errno.h>
#define SHMSZ 27
int main(void) {
exchange_T* shm;
int producer, consumer, i;
key_t key1 = 123, key2 = 456;
consumer = open_semaphore_set(key1, 1);
procuder = open_semaphore_set(key2, 1);
shm = (exchange_T*)shminit(ftock("shared", 0));
for (i = 0; ; i++) {
semaphore_V(consumer);
semaphore_P(producer);
printf("Data recieived:%s, sequence:%d/n", shm->buf, shm->seg);
if (strncmp(shm->buf, "end", 3) == 0)
break;
}
rm_semaphore(producer);
rm_semaphore(consumer);
exit(0);
}
server.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <errno.h>
#include "outshm.h"
#define SHMSZ 27
int main(void) {
exchage_T *shm, *s;
int producer, consumer, i;
char readbuf[BUFSIZE];
key_t key1 = 123, key2 = 456;
consumer = open_semaphore_set(key1, 1);
init_a_semaphore(consumer, 0, 0);
producer = open_semaphore_set(key2, 1);
init_a_semaphore(producer, 0, 0);
shm = (exchange_T*)shminit(ftok("shared", 0));
for(i = 0; ; i++) {
printf("Enter some text:");
fgets(readbuf, BUFSIZE, stdin);
semaphore_P(consumer);
shm->seq = i;
sprintf(shm->buf, "%s", readbuf);
semaphore_V(producer);
if(strncmp(readbuf, "end", 3) == 0)
break;
}
exit(0);
}