一、共享内存介绍:
共享内存:
允许两个不相关的进程访问同一个逻辑内存,即在两个正在运行的进程之间共享和传递数据。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
shmem相关函数:
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmdt(const void *shm_addr);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
1.shmget()函数:创建共享内存
int shmget(key_t key, size_t size, int shmflg);
参数:
key : 为共享内存段命名。有一个特殊的键值IPC_PRIVATE, 用于创建一个只属于创建进程的共享内存。
size: 以字节为单位指定需要共享的内存容量。
shmflag: 包含9个比特的权限标志,作用与创建文件时的mode标志一样。由IPC_CREAT定义的一个特殊比特,须和权限标志按位或才能创建一个新的共享内存段。
NOTE:
权限标志对共享内存非常有用,因为它允许一个进程创建的共享内存可以被共享内存的创建者所拥有的进程写入,同时其它用户创建的进程只能读取共享内存。
可利用该功能来提供一种有效的对数据进行只读访问的方法,通过将数据放共享内存并设置它的权限,以避免数据被其他用户修改。
返回值:
创建成功,则返回一个非负整数,即共享内存标识;
如果失败,则返回-1.
2.shmat()函数:连接共享内存到当前进程地址空间
第一次创建共享内存段时,它不能被任何进程访问。要想启动对该内存的访问,必须将其连接到一个进程的地址空间。
void *shmat(int shm_id, const void *shm_addr, int shmflg);
参数:
shm_id : 由shmget返回的共享内存标识。
shm_add: 指定共享内存连接到当前进程中的地址位置。它通常是一个空指针, 表示让系统来选择共享内存出现的地址。
shmflg : 是一组标志。
它的两个可能取值是:
SHM_RND和shm_add联合使用,控制共享内存连接的地址。
SHM_RDONLY:使连接的内存只读
返回值:
如果调用成功, 返回一个指向共享内存第一个字节的指针;
如果失败,返回-1.
共享内存的读写权限由它的属主(共享内存的创建者),它的访问权限和当前进程的属主决定。共享内存的访问权限类似于文件的访问权限。
3.shmdt()函数:将共享内存从当前进程中分离。
int shmdt(const void *shm_addr);
shm_addr: shmat()函数返回的地址指针。
成功时,返回0,
失败时,返回-1.
NOTE:
共享内存分离并未删除它,
只是使得该共享内存对当前进程不再可用。
4.shmctl()函数:共享内存的控制函数
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
shmid_ds结构至少包含以下成员:
struct shmid_ds {
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}
参数:
shm_id : 是shmget返回的共享内存标识符。
command: 是要采取的动作,它可以取3个值:
IPC_STAT 把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET 如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID 删除共享内存段
buf : 是一个指针,包含共享内存模式和访问权限的结构。
返回值:
成功时,返回0,
失败时,返回-1.
二、共享内存使用方法:
两个程序如何使用同一块共享内存?
1.两个程序的shmget()函数传入相同的第一个参数key即可。
2.两个程序通过shmat()把同一块共享内存连接到各自当前进程的地址空间上。shmat()的第一个参数shm_id是由shmget()函数返回的共享内存标识。
使用实例一:
We develop two programs here that illustrate the passing of a simple piece of memory (a string) between the processes if running simultaneously.
源文件shm_server.c的源代码如下:
/*
* shm_server - creates the string and shared memory portion.
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#define SHMSZ 27
main()
{
char c;
int shmid;
key_t key;
char *shm, *s;
/*
* We'll name our shared memory segment
* "5678".
*/
key = 5678;
/*
* Create the segment.
*/
if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) {
perror("shmget");
exit(1);
}
/*
* Now we attach the segment to our data space.
*/
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
exit(1);
}
/*
* Now put some things into the memory for the
* other process to read.
*/
s = shm;
printf("put some things into the memory for the other process to read:\n");
for (c = 'a'; c <= 'z'; c++) {
*s++ = c;
printf("%c", c);
}
printf("\n");
*s = NULL;
/*
* Finally, we wait until the other process
* changes the first character of our memory
* to '*', indicating that it has read what
* we put there.
*/
while (*shm != '*') {
sleep(1);
}
exit(0);
}
源文件shm_client.c的源代码如下:
/*
* shm_client - client program to demonstrate shared memory.
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#define SHMSZ 27
main()
{
int shmid;
key_t key;
char *shm, *s;
/*
* We need to get the segment named
* "5678", created by the server.
*/
key = 5678;
/*
* Locate the segment.
*/
if ((shmid = shmget(key, SHMSZ, 0666)) < 0) {
perror("shmget");
exit(1);
}
/*
* Now we attach the segment to our data space.
*/
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
exit(1);
}
/*
* Now read what the server put in the memory.
*/
printf("Now read what the server put in the memory:\n");
for (s = shm; *s != NULL; s++) {
putchar(*s);
}
putchar('\n');
/*
* Finally, change the first character of the
* segment to '*', indicating we have read
* the segment.
*/
*shm = '*';
exit(0);
}
分别在两个终端下编译后运行:
[root@localhost key]# ./shm_server
put some things into the memory for the other process to read:
abcdefghijklmnopqrstuvwxyz
[root@localhost key]# ./shm_client
Now read what the server put in the memory:
abcdefghijklmnopqrstuvwxyz
使用实例二:
首先使用shmget()建立一块共享内存, 同时向该内存中写入数据并返回该共享内存shmid, 然后另一个程序使用上一程序返回的shmid,读取该共享内存中的数据。
源文件server.c的源代码如下:
//建立共享内存并写入数据的程序
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>
void get_buf(char *buf)
{
int i = 0;
while((buf[i]=getchar()) != '\n' && i<1024)
{
i++;
}
}
int main(void)
{
int shmid;
char *buf;
shmid = shmget(IPC_PRIVATE,sizeof(char)*1024,IPC_CREAT|0666);
if(shmid == -1)
{
perror("shmget");
}
if((int)(buf=shmat(shmid,NULL,0)) == -1)
{
perror("shmat");
exit(1);
}
printf("input string to share memory: \n");
get_buf(buf);
printf("share memory ID:%d\n", shmid);
return 0;
}
源文件client.c的源代码如下:
//读取数据的程序
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
int shmid;
char *buf;
shmid = atoi(argv[1]);
if((int)(buf=shmat(shmid,NULL,0)) == -1)
{
perror("shmat");
exit(1);
}
printf("print share memory content: %s\n", buf);
shmdt(buf);
return 0;
}
分别在两个终端下编译后运行:
[root@localhost share_memory]# ./server
input string:
hello world
share memory ID:425997
命令行的参数为第一个程序输出的共享内存ID
[root@localhost share_memory]# ./client425997
print share memory content: hello world
使用完以后可使用如下命令删除该共享内存:
ipcrm -m 19562507