unix下共享内存
2006-04-30 10:00
211 查看
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。
采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。
(2)
共享内存 (shared memory)是 Unix下的多进程之间的通信方法 ,这种方法通常用于一个程序的多进程间通信,实际上多个程序间也可以通过共享内存来传递信息。本文介绍如何在 Client/Server方式下实现多个程序间共享内存。
问题分析
多个程序之间共享内存 ,首先要解决的问题是怎样让各个程序能够访问同一块内存和相同的信号量。共享内存的 id可以通过调用 shmget(key_t key, size_t size, int shmflg)函数取得;信号量的 id可以通过调用 semget(key_t key, int nsems, int semflg)函数取得。实际上,只要在调用这两个函数时使用相同的 key值,各程序之间就能达到共享内存的目的。 Unix通过调用 key_t ftok(const char* path, int id)函数来产生 key值 ,如果各程序都用同样的参数来调用此函数,自然也就得到相同的 key值了。例子中各个程序都使用 key=ftok(" /", 0)得到相同的 key值 ,再进而由 key值得到相同的共享内存 id和信号量 id。
第二个要解决的问题是如何控制多个程序并发访问共享内存。本文的例子模拟在 Client/Server方式下,由一个 Server产生数据,多个 Client去读取数据的操作。常规的方法是设一个信号量,将访问共享内存的程序作为临界区来处理。程序进入时用 p()操作取得锁,退出时用 v()操作释放锁。但这样做有两个问题:一是这样各个程序就处于平等的地位,而实际中往往 Server的优先级应该比 Client更高。比如,在股票行情应用程序中 ,共享内存里存放行情信息, Server负责定时更新; Client是 CGI程序,负责按客户要求读取共享内存中的数据,然后再反馈给客户。在这种情况下, Server就不能等所有 Client进程都读完了才开始写,因为这样 Client取得的数据反而是过时的。二是各个 Client之间由于都是读操作,所以没有必要互斥。
本文对这两个问题的解决方案是:只有 Server进行 p()、 v()操作,信号量初始值设为 0, p()操作将它加一, v()操作将它减一; Client读共享内存之前要先等待信号量的值为 0,这样 Server的 p()操作总是成功,而 Server的 p()操作后,尚未进入临界区的 Client只能等到 Server执行 v()操作后才能读。这样 Server比 Client优先, Client之间不互斥。但这样又产生另一个问题:一个 Server开始写时,部分 Client可能已经进入临界区,有可能出现读不完整的问题。因此,例子基于这样一个前提: Client程序比较简单,不会被阻塞,并且能够在一个时间片内执行完读取操作。本例中处于临界区中的 Client数目是有限的,如果 Server等待一个时间片 (例子中是等待一分钟 )后, Client就能全部退出临界区,这个问题就能排除。很多 CGI程序能够满足这个假设条件,如果 Client确实不满足条件,可以生成访问共享内存的子进程,它的执行时间应该满足上述要求。
应用实例
下面给出实现多程序间共享内存的例子程序的部分代码:
1.Server端程序
# define SEGSIZE 1024
# define READTIME 1
union semun {
int val;
struct semid_ds* buf;
ushort_t* array;
} ;
//生成信号量
int sem(key_t key){
union semun sem ;
int semid;
sem.val=0;
semid=semget(key,1,IPC_CREAT|0666);
if (semid ==- 1){
printf(" create semaphore error/n" );
exit(- 1);
}
//初始化信号量
semctl(semid,0,SETVAL,sem);
return semid;
}
//删除信号量
void d_sem(int semid){
union semun sem ;
sem.val=0;
semctl(semid,0,IPC_RMID,0);
}
int p(int semid){
struct sembuf sops={0,+ 1,IPC_NOWAIT};
return(semop(semid,& sops,1));
}
int v(int semid){
struct sembuf sops={0,- 1,IPC_NOWAIT};
return(semop(semid,& sops,1));
}
int main(){
key_t key;
int shmid,semid;
char* shm;
char msg[7]=" data " ;
char i;
struct shmid_ds buf;
key=ftok(" /" , 0);
shmid=shmget(key,SEGSIZE,IPC_CREAT|0604);
if(shmid ==- 1){
printf(" create shared momery error/n" );
return- 1;
}
shm=(char* )shmat(shmid,0,0);
if((int)shm ==- 1){
printf(" attach shared momery error/n" );
return- 1;
}
semid=sem(key);
for(i=0;i<=3;i++ ){
sleep(1);
p(semid);
sleep(READTIME);
msg[5]=' 0'+ i;
memcpy(shm,msg,sizeof(msg));
sleep(58);
v(semid);
}
shmdt(shm);
shmctl(shmid,IPC_RMID,& buf);
d_sem(semid);
return 0;
}
2.Client端程序# define SEGSIZE 1024
union semun {
int val;
struct semid_ds* buf;
ushort_t* array;
} ;
//打印程序执行时间
void secondpass(){
static long start=0;
time_t timer;
if (start == 0){
timer=time(NULL);
start=(long)timer;
printf(" now start /n" );
}
printf(" second:% ld /n" ,(long)(time(NULL))- start);
}
int sem(key_t key){
union semun sem ;
int semid;
sem.val=0;
semid=semget(key,0,0);
if (semid ==- 1){
printf(" get semaphore error/n");
exit(- 1);
}
return semid;
}
//等待信号量变成 0
void waitv(int semid){
struct sembuf sops={0,0,0};
semop(semid,& sops,1);
}
int main(){
key_t key;
int shmid,semid;
char* shm;
char msg[100];
int i;
key=ftok(" /" , 0);
shmid=shmget(key,SEGSIZE,0);
if(shmid ==- 1){
printf(" get shared momery error/n" );
return- 1;
}
shm=(char* )shmat(shmid,0,0);
if((int)shm ==- 1){
printf(" attach shared momery error/n" );
return- 1;
}
semid=sem(key);
for(i=0;i< 3;i++ ){
sleep(2);
waitv(semid);
printf(" the msg get is /n% s/n" ,shm+ 1);
secondpass();
}
shmdt(shm);
return 0;
}
待续……
//----------------------------------------------------------------
// Version: 1.0.0
// Author: Zhangcf@lianchuang.com
// Copyright: copyleft,free
// Purpose: SharedMem
// Date: 2004-9-3
//*********************************************
#ifndef CSHAREDMEM_H_
#define CSHAREDMEM_H_
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
class CSharedMem
{
public:
union semun
{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
CSharedMem();
~CSharedMem();
private:
//生成信号量
int Create_Sem(key_t key);
//删除信号量
void Delete_sem();
int p();
int v();
int m_nSegSize; //共享内容大小
char* m_pszContent; //映射区指针
int m_nSemid; //创建信号Key
pthread_mutex_t m_hMutex; //线程锁
public:
bool InitSegSize( int nSegSize=1024 );
bool Read(char* pszContent, int nReadByteNum, bool bDeleteContent=true);
bool Writer(char* pszContent, int nWriterByteNum);
};
#endif //CSHAREDMEM_H_
******************************************************************
******************************************************************
******************************************************************
******************************************************************
//*********************************************
// Version: 1.0.0
// Author: Zhangcf@lianchuang.com
// Copyright: copyleft,free
// Purpose: SharedMem
// Date: 2004-9-3
//*********************************************
#include "SharedMem.h"
CSharedMem::CSharedMem()
{
m_pszContent = NULL;
/* 用默认属性初始化一个互斥锁对象*/
pthread_mutex_init (&m_hMutex,NULL);
printf("/nCSharedMem Start ...");
}
CSharedMem::~CSharedMem()
{
munmap( m_pszContent, sizeof(char)*m_nSegSize);
printf(" /n umap OK /n");
Delete_sem();
pthread_mutex_destroy(&m_hMutex);
printf("/nCSharedMem End /n");
}
//生成信号量
int CSharedMem::Create_Sem(key_t kKey)
{
union semun sem ;
sem.val=0;
m_nSemid = semget(IPC_PRIVATE, 1, IPC_CREAT|0666);
if (m_nSemid == -1)
{
printf(" create semaphore error/n" );
return -1;
}
//初始化信号量
semctl(m_nSemid, 0, SETVAL, sem);
return m_nSemid;
}
//删除信号量
void CSharedMem::Delete_sem()
{
union semun sem ;
sem.val=0;
semctl(m_nSemid, 0, IPC_RMID, 0);
}
int CSharedMem::p()
{
//struct sembuf sops={0, +1,IPC_NOWAIT};
//return(semop(m_nSemid, &sops, 1));
printf("/n开始进入锁定.../n");
pthread_mutex_lock(&m_hMutex);
printf("...已经锁定/n");
}
int CSharedMem::v()
{
//struct sembuf sops={0, -1,IPC_NOWAIT};
//return(semop(m_nSemid, &sops, 1));
printf("/n开始进入解锁.../n");
pthread_mutex_unlock(&m_hMutex);
printf("...已经解锁/n");
}
bool CSharedMem::InitSegSize( int nSegSize )
{
// 初始化信号量
key_t kKey;
kKey = ftok("/", 0);
int nRet = Create_Sem( kKey );
if( nRet == -1 )
return false;
char* pFileName = "./SharedMem1111";
m_nSegSize = nSegSize;
// 移至文件尾,加上'/0'字符
int fd = open(pFileName, O_CREAT|O_APPEND|O_RDWR, 0777);
lseek(fd, sizeof(char)*m_nSegSize-1, SEEK_SET);
write(fd, "", 1);
void* pVoid = mmap( NULL, sizeof(char)*m_nSegSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
if( pVoid == MAP_FAILED )
{
printf(" Mmap ERROR 1/n");
return false;
}
m_pszContent = (char*)pVoid;
//strncpy(m_pszContent, "abcdef", 5);
printf(" /n initiallize over:%s /n",m_pszContent);
return true;
}
bool CSharedMem::Read(char* pszContent, int nReadByteNum, bool bDeleteContent)
{
p();
memcpy(pszContent, m_pszContent, nReadByteNum);
pszContent[nReadByteNum] = '/0';
if( bDeleteContent )
memset(m_pszContent, '/0', bDeleteContent);
v();
return true;
}
bool CSharedMem::Writer(char* pszContent, int nWriterByteNum)
{
p();
strncat(m_pszContent, pszContent, nWriterByteNum);
v();
return true;
}
采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。
(2)
共享内存 (shared memory)是 Unix下的多进程之间的通信方法 ,这种方法通常用于一个程序的多进程间通信,实际上多个程序间也可以通过共享内存来传递信息。本文介绍如何在 Client/Server方式下实现多个程序间共享内存。
问题分析
多个程序之间共享内存 ,首先要解决的问题是怎样让各个程序能够访问同一块内存和相同的信号量。共享内存的 id可以通过调用 shmget(key_t key, size_t size, int shmflg)函数取得;信号量的 id可以通过调用 semget(key_t key, int nsems, int semflg)函数取得。实际上,只要在调用这两个函数时使用相同的 key值,各程序之间就能达到共享内存的目的。 Unix通过调用 key_t ftok(const char* path, int id)函数来产生 key值 ,如果各程序都用同样的参数来调用此函数,自然也就得到相同的 key值了。例子中各个程序都使用 key=ftok(" /", 0)得到相同的 key值 ,再进而由 key值得到相同的共享内存 id和信号量 id。
第二个要解决的问题是如何控制多个程序并发访问共享内存。本文的例子模拟在 Client/Server方式下,由一个 Server产生数据,多个 Client去读取数据的操作。常规的方法是设一个信号量,将访问共享内存的程序作为临界区来处理。程序进入时用 p()操作取得锁,退出时用 v()操作释放锁。但这样做有两个问题:一是这样各个程序就处于平等的地位,而实际中往往 Server的优先级应该比 Client更高。比如,在股票行情应用程序中 ,共享内存里存放行情信息, Server负责定时更新; Client是 CGI程序,负责按客户要求读取共享内存中的数据,然后再反馈给客户。在这种情况下, Server就不能等所有 Client进程都读完了才开始写,因为这样 Client取得的数据反而是过时的。二是各个 Client之间由于都是读操作,所以没有必要互斥。
本文对这两个问题的解决方案是:只有 Server进行 p()、 v()操作,信号量初始值设为 0, p()操作将它加一, v()操作将它减一; Client读共享内存之前要先等待信号量的值为 0,这样 Server的 p()操作总是成功,而 Server的 p()操作后,尚未进入临界区的 Client只能等到 Server执行 v()操作后才能读。这样 Server比 Client优先, Client之间不互斥。但这样又产生另一个问题:一个 Server开始写时,部分 Client可能已经进入临界区,有可能出现读不完整的问题。因此,例子基于这样一个前提: Client程序比较简单,不会被阻塞,并且能够在一个时间片内执行完读取操作。本例中处于临界区中的 Client数目是有限的,如果 Server等待一个时间片 (例子中是等待一分钟 )后, Client就能全部退出临界区,这个问题就能排除。很多 CGI程序能够满足这个假设条件,如果 Client确实不满足条件,可以生成访问共享内存的子进程,它的执行时间应该满足上述要求。
应用实例
下面给出实现多程序间共享内存的例子程序的部分代码:
1.Server端程序
# define SEGSIZE 1024
# define READTIME 1
union semun {
int val;
struct semid_ds* buf;
ushort_t* array;
} ;
//生成信号量
int sem(key_t key){
union semun sem ;
int semid;
sem.val=0;
semid=semget(key,1,IPC_CREAT|0666);
if (semid ==- 1){
printf(" create semaphore error/n" );
exit(- 1);
}
//初始化信号量
semctl(semid,0,SETVAL,sem);
return semid;
}
//删除信号量
void d_sem(int semid){
union semun sem ;
sem.val=0;
semctl(semid,0,IPC_RMID,0);
}
int p(int semid){
struct sembuf sops={0,+ 1,IPC_NOWAIT};
return(semop(semid,& sops,1));
}
int v(int semid){
struct sembuf sops={0,- 1,IPC_NOWAIT};
return(semop(semid,& sops,1));
}
int main(){
key_t key;
int shmid,semid;
char* shm;
char msg[7]=" data " ;
char i;
struct shmid_ds buf;
key=ftok(" /" , 0);
shmid=shmget(key,SEGSIZE,IPC_CREAT|0604);
if(shmid ==- 1){
printf(" create shared momery error/n" );
return- 1;
}
shm=(char* )shmat(shmid,0,0);
if((int)shm ==- 1){
printf(" attach shared momery error/n" );
return- 1;
}
semid=sem(key);
for(i=0;i<=3;i++ ){
sleep(1);
p(semid);
sleep(READTIME);
msg[5]=' 0'+ i;
memcpy(shm,msg,sizeof(msg));
sleep(58);
v(semid);
}
shmdt(shm);
shmctl(shmid,IPC_RMID,& buf);
d_sem(semid);
return 0;
}
2.Client端程序# define SEGSIZE 1024
union semun {
int val;
struct semid_ds* buf;
ushort_t* array;
} ;
//打印程序执行时间
void secondpass(){
static long start=0;
time_t timer;
if (start == 0){
timer=time(NULL);
start=(long)timer;
printf(" now start /n" );
}
printf(" second:% ld /n" ,(long)(time(NULL))- start);
}
int sem(key_t key){
union semun sem ;
int semid;
sem.val=0;
semid=semget(key,0,0);
if (semid ==- 1){
printf(" get semaphore error/n");
exit(- 1);
}
return semid;
}
//等待信号量变成 0
void waitv(int semid){
struct sembuf sops={0,0,0};
semop(semid,& sops,1);
}
int main(){
key_t key;
int shmid,semid;
char* shm;
char msg[100];
int i;
key=ftok(" /" , 0);
shmid=shmget(key,SEGSIZE,0);
if(shmid ==- 1){
printf(" get shared momery error/n" );
return- 1;
}
shm=(char* )shmat(shmid,0,0);
if((int)shm ==- 1){
printf(" attach shared momery error/n" );
return- 1;
}
semid=sem(key);
for(i=0;i< 3;i++ ){
sleep(2);
waitv(semid);
printf(" the msg get is /n% s/n" ,shm+ 1);
secondpass();
}
shmdt(shm);
return 0;
}
待续……
//----------------------------------------------------------------
UNIX下共亨内存
//*********************************************// Version: 1.0.0
// Author: Zhangcf@lianchuang.com
// Copyright: copyleft,free
// Purpose: SharedMem
// Date: 2004-9-3
//*********************************************
#ifndef CSHAREDMEM_H_
#define CSHAREDMEM_H_
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
class CSharedMem
{
public:
union semun
{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
CSharedMem();
~CSharedMem();
private:
//生成信号量
int Create_Sem(key_t key);
//删除信号量
void Delete_sem();
int p();
int v();
int m_nSegSize; //共享内容大小
char* m_pszContent; //映射区指针
int m_nSemid; //创建信号Key
pthread_mutex_t m_hMutex; //线程锁
public:
bool InitSegSize( int nSegSize=1024 );
bool Read(char* pszContent, int nReadByteNum, bool bDeleteContent=true);
bool Writer(char* pszContent, int nWriterByteNum);
};
#endif //CSHAREDMEM_H_
******************************************************************
******************************************************************
******************************************************************
******************************************************************
//*********************************************
// Version: 1.0.0
// Author: Zhangcf@lianchuang.com
// Copyright: copyleft,free
// Purpose: SharedMem
// Date: 2004-9-3
//*********************************************
#include "SharedMem.h"
CSharedMem::CSharedMem()
{
m_pszContent = NULL;
/* 用默认属性初始化一个互斥锁对象*/
pthread_mutex_init (&m_hMutex,NULL);
printf("/nCSharedMem Start ...");
}
CSharedMem::~CSharedMem()
{
munmap( m_pszContent, sizeof(char)*m_nSegSize);
printf(" /n umap OK /n");
Delete_sem();
pthread_mutex_destroy(&m_hMutex);
printf("/nCSharedMem End /n");
}
//生成信号量
int CSharedMem::Create_Sem(key_t kKey)
{
union semun sem ;
sem.val=0;
m_nSemid = semget(IPC_PRIVATE, 1, IPC_CREAT|0666);
if (m_nSemid == -1)
{
printf(" create semaphore error/n" );
return -1;
}
//初始化信号量
semctl(m_nSemid, 0, SETVAL, sem);
return m_nSemid;
}
//删除信号量
void CSharedMem::Delete_sem()
{
union semun sem ;
sem.val=0;
semctl(m_nSemid, 0, IPC_RMID, 0);
}
int CSharedMem::p()
{
//struct sembuf sops={0, +1,IPC_NOWAIT};
//return(semop(m_nSemid, &sops, 1));
printf("/n开始进入锁定.../n");
pthread_mutex_lock(&m_hMutex);
printf("...已经锁定/n");
}
int CSharedMem::v()
{
//struct sembuf sops={0, -1,IPC_NOWAIT};
//return(semop(m_nSemid, &sops, 1));
printf("/n开始进入解锁.../n");
pthread_mutex_unlock(&m_hMutex);
printf("...已经解锁/n");
}
bool CSharedMem::InitSegSize( int nSegSize )
{
// 初始化信号量
key_t kKey;
kKey = ftok("/", 0);
int nRet = Create_Sem( kKey );
if( nRet == -1 )
return false;
char* pFileName = "./SharedMem1111";
m_nSegSize = nSegSize;
// 移至文件尾,加上'/0'字符
int fd = open(pFileName, O_CREAT|O_APPEND|O_RDWR, 0777);
lseek(fd, sizeof(char)*m_nSegSize-1, SEEK_SET);
write(fd, "", 1);
void* pVoid = mmap( NULL, sizeof(char)*m_nSegSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
if( pVoid == MAP_FAILED )
{
printf(" Mmap ERROR 1/n");
return false;
}
m_pszContent = (char*)pVoid;
//strncpy(m_pszContent, "abcdef", 5);
printf(" /n initiallize over:%s /n",m_pszContent);
return true;
}
bool CSharedMem::Read(char* pszContent, int nReadByteNum, bool bDeleteContent)
{
p();
memcpy(pszContent, m_pszContent, nReadByteNum);
pszContent[nReadByteNum] = '/0';
if( bDeleteContent )
memset(m_pszContent, '/0', bDeleteContent);
v();
return true;
}
bool CSharedMem::Writer(char* pszContent, int nWriterByteNum)
{
p();
strncat(m_pszContent, pszContent, nWriterByteNum);
v();
return true;
}
相关文章推荐
- Unix System V共享内存学习笔记(共享内存、信号量)
- UNIX 共享内存应用中的问题及解决方法
- 用 ipcs 管理oracle unix 共享内存
- UNIX 共享内存应用中的问题及解决方法
- Unix下共享内存机制 unsigned short 与 char 互相转换 GCC编译共享内存
- 【2016/1】 Unix IPC 信号 共享内存 消息队列
- unix 编程 共享内存总结
- 转载:UNIX系统共享内存的应用编程技术
- UNIX 共享内存应用中的问题及解决方法
- UNIX 共享内存应用中的问题及解决方法
- Unix/Linux下的IPC---共享内存
- unix/linux下的共享内存、信号量、队列信息管理(进程间通信)
- linux/Unix 共享内存 mmap() 函数使用的一个问题
- 对话 UNIX: 通过共享内存进行进程间通信
- Linux\Unix IPC进程通信实例分析(一):共享内存通信---系统V
- UNIX: 通过共享内存进行进程间通信
- Unix/linux 进程间通讯 - 共享内存
- unix/linux下的共享内存、信号量、队列信息管理
- 对话 UNIX: 通过共享内存进行进程间通信
- Unix中多程序间共享内存