目录
共享内存
定义
共享内存的同步机制
shmget函数
shmat函数
shmdt函数
shmctl函数
不使用信号量对共享内存进行同步操作
使用信号量对共享内存进行同步操作
共享内存允许两个不相关的进程访问同一块逻辑内存。因为数据不需要在进程之间复制,所以共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式。大多数共享内存的具体实现,都把由不同进程之间共享的内存安排为同一段物理内存。共享内存是由IPC为进程创建的一个特殊的地址范围,它将出现在该进程的地址空间中。其它进程可以将同一段共享内存连接到它们自己的地址空间中。所以进程都可以访问共享内存中的地址,就像是每个进程中malloc出的地址一样。如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。
共享内存为在多个进程间共享和传递数据提供了一种有效方式,但并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问,一般用共享内存来提供对大块内存区域的有效访问。
未提供同步机制表现在,如果在第一个进程结束对共享内存的写操作之前,并没有自动的机制可以阻止第二个进程对共享内存进行读取。所以通常,信号量被用来实现对共享内存访问的同步。同步之后使得,如果一个进程正在将数据放入共享内存,则在它做完写操作之前,别的进程不能去取这些数据。
//共享内存使用的函数类似于信号量的函数,定义如下:
#include
void *shmat(int shm_id,const void* shm_addr,int shmflg);
int shmctl(int shm_id,int cmd,struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key,size_t size,int shmflg);
用shmget函数来创建一个新的共享内存,或者获取一个已有的共享内存。
void *shmat(int shm_id,const void* shm_addr,int shmflg);
①与信号量一样,程序需要提供一个参数key,它有效地为共享内存段命名。特殊的键值IPC_PRIVATE,它用来创建一个只属于创建进程的共享内存。
②size以字节为单位指定需要共享的内存容量。
③shmflg为权限标志,与创建文件时使用的mode标准一样。权限标志对共享内存非常有用,因为允许一个进程创建的共享内存可以被共享内存的创建者所拥有的进程写入,同时其他进程只能读取该共享内存。可以利用这种功能来提供一种有效的对数据进行只读访问的方法,通过将数据放入共享内存并设置它的权限,就可以避免数据被其他用户修改。
共享内存创建成功,shmget函数返回一个共享内存标识符(非负整数),这个标识符将用于后续的共享内存函数。
用shmget函数将共享内存映射到进程地址空间。
void *shmat(int shm_id,const void* shm_addr,int shmflg);
①shm_id是shmget返回的共享内存标识符。
②shm_addr指定共享内存连接到当前进程中的地址位置。通常是一个空指针,表示让系统选择共享内存出现的地址。
③shmflg是一组位标志。两个可能取值为:SHM_RND(这个标志与shm_addr联合使用,用来控制共享内存连接的地址) 和 SHM_RDONLY(它使得连接的内存只读)。
shmflg & SHM_RDONLY 为true时,此时即使该共享内存的访问权限允许写操作,它都不能被写入。
shmat函数调用成功,返回一个指向共享内存第一个字节的指针;如果失败,返回-1。
shmdt函数是将共享内存从当前进程中分离,即断开映射。
int shmdt(const void *shm_addr);
参数shm_addr是shmat返回的地址指针。调用成功返回0,失败返回-1。
注意:将共享内存只是分离,并未删除它,只是使得该共享内存对当前进程不再可用。
shmctl为控制函数
int shmctl(int shm_id,int command,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是一个指针,指向包含共享内存模式和访问权限的结构。
shmctl函数成功时返回0,失败时返回-1。
/* a.c */
#include
#include
#include
#include
#include
#include
#include"sem.h"
int main()
{
int shmid = shmget((key_t)1234,256,IPC_CREAT|0600);//创建共享内存
assert(shmid != -1);
char *s = (char*)shmat(shmid,NULL,0);//将共享内存映射到地址空间
while(1)
{
char buff[128] = {0};
printf("input:\n");
fgets(buff,128,stdin);//循环输入,相当于对共享内存进行写操作
strcpy(s,buff);//buff --> s
if(strncmp(buff,"end",3) == 0)
{
break;
}
}
shmdt(s);//断开映射
exit(0);
}
#include
#include
#include
#include
#include
#include
int main()
{
int shmid = shmget((key_t)1234,256,IPC_CREAT|0600);
assert(shmid != -1);
char *s = (char*)shmat(shmid,NULL,0);
assert(s != NULL);
while(1)
{
if(strncmp(s,"end",3) == 0)
{
break;
}
printf("s=%s\n",s);//打印读取到共享内存中的值
sleep(1);
}
shmdt(s);//断开映射
if(shmctl(shmid,IPC_RMID,NULL) == -1)
{
perror("shmctl error");
}
exit(0);
}
没有同步操作时运行结果如上图,可以分析出进程a每写一次,共享内存已经被进程b访问了多次。
/* sem.h */
#include
#include
#include
#include
#include
#include
void sem_init();
void sem_p();
void sem_v();
void sem_destroy();
union semun
{
int val;
};
#include"sem.h"
#include
#include
#include
#include
#include
#include
static int semid;
void sem_init()
{
semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//创建一个新的信号量
if(semid == -1)//创建失败
{
semid = semget((key_t)1234,1,IPC_CREAT|0600);//获取这个键值对应的已有的信号量
if(semid == -1)//获取失败
{
perror("semget error");
return;
}
}
else
{
union semun a;//semun --> semctl
a.val = 1;
if(semctl(semid,0,SETVAL,a) == -1)//给信号量赋初始值
{
perror("semctl error");
}
}
}
void sem_p()
{
struct sembuf buf;//sembuf ---> semget
buf.sem_num = 0;//第一个信号
buf.sem_op = -1;//p操作,信号量-1
buf.sem_flg = SEM_UNDO;
if(semop(semid,&buf,1) == -1)
{
perror("semop p error");
}
}
void sem_v()
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;//v操作,信号量+1
buf.sem_flg = SEM_UNDO;
if(semop(semid,&buf,1) == -1)
{
perror("semop v error");
}
}
void sem_destroy()
{
if(semctl(semid,0,IPC_RMID) == -1)//IPC_RMID删除一个已经无需继续使用的信号量标识符
{
perror("semctl error");
}
}
#include
#include
#include
#include
#include
#include
#include"sem.h"
int main()
{
int shmid = shmget((key_t)1234,256,IPC_CREAT|0600);//创建共享内存
assert(shmid != -1);
char *s = (char*)shmat(shmid,NULL,0);
sem_init();
while(1)
{
char buff[128] = {0};
printf("input:\n");
fgets(buff,128,stdin);
sem_p(0);//p(s1)
strcpy(s,buff);
sem_v(1);//v(s2)
if(strncmp(buff,"end",3) == 0)
{
break;
}
}
shmdt(s);
exit(0);
}
#include
#include
#include
#include
#include
#include
int main()
{
int shmid = shmget((key_t)1234,256,IPC_CREAT|0600);
assert(shmid != -1);
char *s = (char*)shmat(shmid,NULL,0);
assert(s != NULL);
sem_init();
while(1)
{
sem_p(1);//p(s2)
if(strncmp(s,"end",3) == 0)
{
break;
}
printf("s=%s\n",s);
sem_v(0);//v(s1);
sleep(1);
}
shmdt(s);
sem_destroy();//destory semaphore
if(shmctl(shmid,IPC_RMID,NULL) == -1)//删除共享内存
{
perror("shmctl error");
}
exit(0);
}
使用信号量对共享内存进行同步操作之后,使得每次对共享内存的访问都是在当前进程写完之后,在它做完写操作之前,别的进程不能去取这些数据。