所有的函数共用头文件
?1 2 3 |
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
|
创建共享内存——>shmget() 函数
?1 2 |
int
shmget(key_t key,
size_t
size,
int
shmflg);
//成功返回共享内存的ID,出错返回-1
|
(1)第一个参数key是长整型(唯一非零),系统建立IPC通讯 ( 消息队列、 信号量和 共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到,由内核变成标识符,要想让两个进程看到同一个信号集,只需设置key值不变就可以。 (2)第二个参数size指定共享内存的大小,它的值一般为一页大小的整数倍(未到一页,操作系统向上对齐到一页,但是用户实际能使用只有自己所申请的大小)。 (3)第三个参数shmflg是一组标志,创建一个新的共享内存,将shmflg 设置了IPC_CREAT标志后,共享内存存在就打开。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的共享内存,如果共享内存已存在,返回一个错误。一般我们会还或上一个文件权限 3.2操作共享内存———>shmctl()函数 ?
1 2 |
int
shmctl(
int
shm_id,
int
cmd,
struct
shmid_ds *buf);
//成功返回0,出错返回-1
|
(1)第一个参数,shm_id是shmget函数返回的共享内存标识符。 (2)第二个参数,cmd是要采取的操作,它可以取下面的三个值 : IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。 IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值 IPC_RMID:删除共享内存段 (3)第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。 shmid_ds结构至少包括以下成员 ?
1 2 3 4 5 6 |
struct
shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
|
创建共享存储段之后,将进程连接到它的地址空间
?1 2 |
void
*shmat(
int
shm_id,
const
void
*shm_addr,
int
shmflg);
//成功返回指向共享存储段的指针,出错返回-1
|
(1)第一个参数,shm_id是由shmget函数返回的共享内存标识。
(2)第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
(3)第三个参数,shm_flg是一组标志位,通常为0
1 2 |
int
shmdt(
const
void
*shmaddr);
//成功返回0,出错返回-1
|
1 2 3 4 5 6 7 8 9 |
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER
#define TEXT_SZ 2048
struct
shared_use_st
{
int
written;
//作为一个标志,非0:表示可读,0表示可写
char
text[TEXT_SZ];
//记录写入和读取的文本
};
#endif
|
源文件shmread.c的源代码如下:
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include "shmdata.h"
int
main()
{
int
running = 1;
//程序是否继续运行的标志
void
*shm = NULL;
//分配的共享内存的原始首地址
struct
shared_use_st *shared;
//指向shm
int
shmid;
//共享内存标识符 //创建共享内存
shmid = shmget((key_t)1234,
sizeof
(
struct
shared_use_st), 0666|IPC_CREAT);
if
(shmid == -1)
{
fprintf
(stderr,
"shmget failed\n"
);
exit
(EXIT_FAILURE);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
if
(shm == (
void
*)-1)
{
fprintf
(stderr,
"shmat failed\n"
);
exit
(EXIT_FAILURE);
}
printf
(
"\nMemory attached at %X\n"
, (
int
)shm);
//设置共享内存
shared = (
struct
shared_use_st*)shm;
shared->written = 0;
while
(running)
//读取共享内存中的数据
{
//没有进程向共享内存定数据有数据可读取
if
(shared->written != 0)
{
printf
(
"You wrote: %s"
, shared->text);
sleep(
rand
() % 3);
//读取完数据,设置written使共享内存段可写
shared->written = 0;
//输入了end,退出循环(程序)
if
(
strncmp
(shared->text,
"end"
, 3) == 0)
running = 0;
}
else
//有其他进程在写数据,不能读取数据
sleep(1);
}
//把共享内存从当前进程中分离
if
(shmdt(shm) == -1)
{
fprintf
(stderr,
"shmdt failed\n"
);
exit
(EXIT_FAILURE);
}
//删除共享内存
if
(shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf
(stderr,
"shmctl(IPC_RMID) failed\n"
);
exit
(EXIT_FAILURE);
}
exit
(EXIT_SUCCESS);
}
|
源文件shmwrite.c的源代码如下:
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shmdata.h"
int
main()
{
int
running = 1;
void
*shm = NULL;
struct
shared_use_st *shared = NULL;
char
buffer[BUFSIZ + 1];
//用于保存输入的文本
int
shmid;
//创建共享内存
shmid = shmget((key_t)1234,
sizeof
(
struct
shared_use_st), 0666|IPC_CREAT);
if
(shmid == -1)
{
fprintf
(stderr,
"shmget failed\n"
);
exit
(EXIT_FAILURE);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, (
void
*)0, 0);
if
(shm == (
void
*)-1)
{
fprintf
(stderr,
"shmat failed\n"
);
exit
(EXIT_FAILURE);
}
printf
(
"Memory attached at %X\n"
, (
int
)shm);
//设置共享内存
shared = (
struct
shared_use_st*)shm;
while
(running)
//向共享内存中写数据
{
//数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本
while
(shared->written == 1)
{
sleep(1);
printf
(
"Waiting...\n"
);
}
//向共享内存中写入数据
printf
(
"Enter some text: "
);
fgets
(buffer, BUFSIZ, stdin);
strncpy
(shared->text, buffer, TEXT_SZ);
//写完数据,设置written使共享内存段可读
shared->written = 1;
//输入了end,退出循环(程序)
if
(
strncmp
(buffer,
"end"
, 3) == 0)
running = 0;
}
//把共享内存从当前进程中分离
if
(shmdt(shm) == -1)
{
fprintf
(stderr,
"shmdt failed\n"
);
exit
(EXIT_FAILURE);
}
sleep(2);
exit
(EXIT_SUCCESS);
}
|
结果截图如下:
分析: 1、程序shmread创建共享内存,然后将它连接到自己的地址空间。在共享内存的开始处使用了一个结构struct_use_st。该结构中有个标志written,当共享内存中有其他进程向它写入数据时,共享内存中的written被设置为0,程序等待。当它不为0时,表示没有进程对共享内存写入数据,程序就从共享内存中读取数据并输出,然后重置设置共享内存中的written为0,即让其可被shmwrite进程写入数据。 2、程序shmwrite取得共享内存并连接到自己的地址空间中。检查共享内存中的written,是否为0,若不是,表示共享内存中的数据还没有被完,则等待其他进程读取完成,并提示用户等待。若共享内存的written为0,表示没有其他进程对共享内存进行读取,则提示用户输入文本,并再次设置共享内存中的written为1,表示写完成,其他进程可对共享内存进行读操作。 四、关于前面的例子的安全性讨论 这个程序是不安全的,当有多个程序同时向共享内存中读写数据时,问题就会出现。可能你会认为,可以改变一下written的使用方式,例如,只有当written为0时进程才可以向共享内存写入数据,而当一个进程只有在written不为0时才能对其进行读取,同时把written进行加1操作,读取完后进行减1操作。这就有点像文件锁中的读写锁的功能。咋看之下,它似乎能行得通。但是这都不是原子操作,所以这种做法是行不能的。试想当written为0时,如果有两个进程同时访问共享内存,它们就会发现written为0,于是两个进程都对其进行写操作,显然不行。当written为1时,有两个进程同时对共享内存进行读操作时也是如些,当这两个进程都读取完是,written就变成了-1. 要想让程序安全地执行,就要有一种进程同步的进制,保证在进入临界区的操作是原子操作。例如,可以使用前面所讲的信号量来进行进程的同步。因为信号量的操作都是原子性的。 五、使用共享内存的优缺点 1、优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。 2、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。