跟消息队列一样,共享内存也有自己的数据结构,如下:
struct shmid_ds { struct ipc_perm shm_perm; /* Ownership and permissions */ size_t shm_segsz; /* Size of segment (bytes) */ time_t shm_atime; /* Last attach time */ time_t shm_dtime; /* Last detach time */ time_t shm_ctime; /* Last change time */ pid_t shm_cpid; /* PID of creator */ pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */ shmatt_t shm_nattch; /* No. of current attaches */ ... };
同样地,第一个成员是共有的IPC内核数据结构,其余是私有成员。
以下是几个共享内存函数:
#include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); void *shmat(int shmid, const void *shmaddr, int shmflg); int shmdt(const void *shmaddr); int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:用来创建共享内存 原型 int shmget(key_t key, size_t size, int shmflg); 参数 key:这个共享内存段名字 size:共享内存大小 shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的 返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
功能:将共享内存段连接到进程地址空间 原型 void *shmat(int shmid, const void *shmaddr, int shmflg); 参数 shmid: 共享内存标识 shmaddr:指定连接的地址 shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY 返回值:成功返回一个指针,指向共享内存第一个字节;失败返回-1
shmaddr为NULL,核心自动选择一个地址 shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。 shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。
公式:shmaddr - (shmaddr % SHMLBA) shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
功能:将共享内存段与当前进程脱离 原型 int shmdt(const void *shmaddr); 参数 shmaddr: 由shmat所返回的指针 返回值:成功返回0;失败返回-1 注意:将共享内存段与当前进程脱离不等于删除共享内存段
功能:用于控制共享内存 原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf); 参数 shmid:由shmget返回的共享内存标识码 cmd:将要采取的动作(有三个可取值) buf:指向一个保存着共享内存的模式状态和访问权限的数据结构 返回值:成功返回0;失败返回-1
cmd 的取值如下,与消息队列类似:
IPC_STAT 把shmid_ds结构中的数据设置为共享内存的当前关联值 IPC_SET 在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值 IPC_RMID 删除共享内存段
下面写两个函数测试一下:
shm_write.c
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
typedef struct stu
{
char name[32];
int age;
} STU;
int main(int argc, char *argv[])
{
int shmid;
shmid = shmget(1234, sizeof(STU), IPC_CREAT | 0666);
if (shmid == -1)
ERR_EXIT("shmget");
STU *p;
p = shmat(shmid, NULL, 0);
if (p == (void *) - 1)
ERR_EXIT("shmat");
strcpy(p->name, "lisi");
p->age = 20;
shmdt(p);
return 0;
}
在上面程序中,先创建一块共享内存,再映射到进程的地址空间。
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./shm_write simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x000004d2 0 simba 666 36 0 可以看到创建了一块共享内存,字节数为写入的STU大小,natth 表示进程连接个数,若在上面程序的shmdt 之前sleep(n); 此时在另一窗口观察,可发现连接数为1,进程退出时,连接数再次为0。
shm_read.c
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
typedef struct stu
{
char name[32];
int age;
} STU;
int main(int argc, char *argv[])
{
int shmid;
shmid = shmget(1234, 0, 0);
if (shmid == -1)
ERR_EXIT("shmget");
STU *p;
p = shmat(shmid, NULL, 0);
if (p == (void *) - 1)
ERR_EXIT("shmat");
printf("name = %s age = %d\n", p->name, p->age);
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
上面程序中,先打开共享内存,若未知共享内存大小,size 可设为0,然后也映射到自身的进程地址空间,读取数据,最后使用shmctl 删除这段共享内存。
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./shm_read name = lisi age = 20 simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status
共享内存段已经被删除。
注:ipcs [-m|-q|-s] ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ]
参考:《UNP》