shared.C
上传用户:shtangtang
上传日期:2007-01-04
资源大小:167k
文件大小:8k
- #include "shared.h"
- #include <string>
- extern "C" {
- # include <sys/types.h>
- # include <sys/stat.h>
- # include <fcntl.h>
- # include <errno.h>
- # include "cloning.h"
- };
- shared_mem::shared_mem()
- {
- _perm = IPC_CREAT|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
- _key_root = new char[80];
- _blocks = 0;
- _work_area._file = 0;
- change_proj( "/tmp/project" );
- }
- shared_mem::shared_mem(const char *p_root)
- {
- _perm = IPC_CREAT|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
- _key_root = new char[80];
- _blocks = 0;
- _work_area._file = 0;
- change_proj( p_root );
- }
- shared_mem::~shared_mem()
- {
- cleanup();
- delete _key_root;
- }
- void shared_mem::change_proj(const char *p_root)
- {
- strncpy( _key_root,p_root,79 );
- }
- void shared_mem::change_perm(int p_perm)
- {
- _perm = p_perm&0777;
- }
- int shared_mem::create_proj(const char *p_id)
- {
- int fd;
- string s = _key_root;
- sto_iterator i;
- _key_lock.lock();
- #ifdef DEBUG
- cout << "create_proj(" << p_id << ")n";
- #endif
- if ( p_id && strlen(p_id) > 0 ) {
- s += "_";
- s += p_id;
- }
- fd = open( s.c_str(),O_CREAT,_perm );
- if ( fd != -1 ) {
- close( fd );
- for( i=_shared.begin();(i != _shared.end());i++ )
- if ( (*i)._file != 0 && strcmp((*i)._file,s.c_str()) == 0 ) {
- _key_lock.unlock();
- return (*i)._id;
- }
- _work_area._id = ++_blocks;
- _work_area._key = -1;
- _work_area._file = strdup( s.c_str() );
- _work_area._proj = 0;
- _work_area._ptr = 0;
- _work_area._page = 0;
- _work_area._mid = 0;
- #ifdef DEBUG
- cout << "new project " << _blocks << endl;
- #endif
- _shared.push_back( _work_area );
- } else
- return -1;
- _key_lock.unlock();
- return _blocks;
- }
- key_t shared_mem::get_key(const char *p_id,int p_proj)
- {
- string s = _key_root;
- key_t key = -1;
- sto_iterator i;
- _key_lock.lock();
- if ( p_id && strlen(p_id) > 0 ) {
- s += "_";
- s += p_id;
- }
- for( i=_shared.begin();(i != _shared.end());i++ )
- if ( strcmp((*i)._file,s.c_str()) == 0 && (*i)._proj == p_proj ) {
- key = (*i)._key;
- break;
- }
- _key_lock.unlock();
- return key;
- }
- key_t shared_mem::make_key(int p_proj)
- {
- sto_iterator k = _shared.end();
- sto_iterator i = _shared.begin();
- _key_lock.lock();
- #ifdef DEBUG
- cout << "make_key(" << p_proj << ")n";
- #endif
- _work_area._key = -1;
- for( ;(i != _shared.end());i++ )
- if ( (*i)._id == p_proj )
- if ( k == _shared.end() || (*i)._key == -1 || (*k)._proj < (*i)._proj )
- k = i;
- if ( k != _shared.end() ) {
- if ( (*k)._key != -1 ) {
- _work_area = (*k);
- if ( ++_work_area._proj != 0 ) {
- _work_area._key = ftok( _work_area._file,_work_area._proj );
- #ifdef DEBUG
- cout << "new key " << _work_area._key << endl;
- #endif
- _shared.push_back( _work_area );
- }
- } else {
- _work_area._key = (*k)._key = ftok( (*k)._file,(*k)._proj );
- #ifdef DEBUG
- cout << "added a key " << (*k)._key << endl;
- #endif
- }
- }
- _key_lock.unlock();
- return _work_area._key;
- }
- list<shared_mem::mem_entry>::iterator
- shared_mem::getmem(sto_iterator i,size_t p_size)
- {
- size_t total_size = ((p_size/PAGE_SIZE)+1)*PAGE_SIZE;
- _mem_entry._size = 0;
- (*i)._mid = shmget( (*i)._key,p_size,IPC_CREAT|_perm );
- if ( (*i)._mid == -1 )
- return _free_list.end();
- (*i)._ptr = shmat( (*i)._mid,0,0 );
- (*i)._page = total_size;
- #ifdef DEBUG
- cout.form("Got a segment %p, sized %dn",(*i)._ptr,(*i)._page);
- #endif
- _mem_entry._size = total_size;
- _mem_entry._data = (*i)._ptr;
- return _free_list.insert( _free_list.end(),_mem_entry );
- }
- void shared_mem::fragment(mem_iterator i,size_t p_size)
- {
- #ifdef DEBUG
- cout.form("Fragmenting %p",(*i)._data);
- cout << " cutting out " << p_size;
- #endif
- _mem_entry._size = p_size;
- _mem_entry._data = (*i)._data;
- (*i)._size -= p_size;
- (*i)._data = (void *)((long)(*i)._data + p_size);
- #ifdef DEBUG
- cout << " redusing it to " << (*i)._size << endl;
- #endif
- }
- void *shared_mem::keyalloc(key_t p_key,size_t p_size)
- {
- void *ptr = 0;
- sto_iterator i;
- // Notice the order of the locks, doing it the other way around
- // may cause a deadlock... see: alloc-fillup
- _mem_lock.lock();
- _key_lock.lock();
- for( i=_shared.begin();(i != _shared.end());i++ )
- if ( (*i)._key == p_key && (*i)._page <= p_size ) {
- ptr = (*i)._ptr;
- break;
- }
- if ( ptr == 0 && i != _shared.end() )
- fragment( getmem(i,p_size),p_size );
- _key_lock.unlock();
- _mem_lock.unlock();
- return ptr;
- }
- void *shared_mem::alloc(size_t p_size,int p_proj)
- {
- mem_iterator i,j;
- _mem_lock.lock();
- #ifdef DEBUG
- cout << "alloc(" << p_size << "," << p_proj << ")n";
- #endif
- j = _free_list.end();
- for ( i=_free_list.begin();(i != _free_list.end());i++ )
- if ( (*i)._size >= p_size )
- if ( j == _free_list.end() || (*i)._size < (*j)._size )
- j = i;
- if ( j == _free_list.end() ) {
- j = fillup(p_size,p_proj);
- if ( j == _free_list.end() ) {
- _mem_lock.unlock();
- return 0; // if we throw an error, we need to cleanup first.
- }
- }
- fragment( j,p_size );
- if ( (*j)._size == 0 )
- _free_list.erase( j );
- _used_list.push_back(_mem_entry);
- _mem_lock.unlock();
- return _mem_entry._data;
- }
- list<shared_mem::mem_entry>::iterator
- shared_mem::fillup(size_t p_size, int p_proj)
- {
- key_t key = -1;
- sto_iterator i;
- #ifdef DEBUG
- cout << "fillup(" << p_size << "," << p_proj << ")n";
- #endif
- if ( p_proj == 0 )
- p_proj = create_proj( 0 );
- for( i=_shared.begin();(i != _shared.end());i++ )
- if ( (*i)._proj == p_proj )
- if ( (*i)._ptr == 0 && (*i)._key != -1 ) {
- key = (*i)._key;
- break;
- }
- if ( key == -1 )
- key = make_key( p_proj );
- if ( key != -1 ) {
- for( i=_shared.begin();(i != _shared.end());i++ )
- if ( (*i)._key == key )
- break;
- return getmem( i,p_size );
- }
- return _free_list.end();
- }
- void shared_mem::dealloc(void *p_ptr)
- {
- struct shmid_ds sm;
- mem_iterator i;
- sto_iterator k;
- _mem_lock.lock();
- for( i=_used_list.begin();(i != _used_list.end());i++ )
- if ( p_ptr == (*i)._data )
- break;
- if ( i == _used_list.end() )
- return; // memory doesn't belong to us, or fragmented.
- _mem_entry = (*i);
- _used_list.erase( i );
- #ifdef DEBUG
- cout.form("Returning %p(%d) ",_mem_entry._data,_mem_entry._size);
- #endif
- // Now collect all consequtive freed memory
- for ( i=_free_list.begin();(i != _free_list.end());i++ ) {
- #ifdef DEBUG
- cout.form(" ?%p(%d)",(*i)._data,(*i)._size);
- #endif
- if ( (*i)._data == (void *)((long)_mem_entry._data - (*i)._size) ) {
- _mem_entry._size += (*i)._size;
- _mem_entry._data = (*i)._data;
- #ifdef DEBUG
- cout << " concatenated";
- #endif
- i = _free_list.erase( i );
- }
- if ( _mem_entry._data == (void *)((long)(*i)._data - _mem_entry._size) ) {
- _mem_entry._size += (*i)._size;
- #ifdef DEBUG
- cout << " attached";
- #endif
- i = _free_list.erase( i );
- }
- #ifdef DEBUG
- cout << endl;
- #endif
- }
- // Now we have a pointer to free memory, now let's see if we
- // should return this memory to the system.
- _key_lock.lock();
- for( k=_shared.begin();(k != _shared.end());k++ )
- if ( (*k)._ptr == _mem_entry._data && (*k)._page == _mem_entry._size ) {
- // Ah ha... we've freed the whole page.
- #ifdef DEBUG
- cout.form("Freeing dereferenced page %p(%d)n",(*k)._ptr,(*k)._page);
- #endif
- shmctl( (*k)._mid,IPC_RMID,&sm );
- shmdt( (*k)._ptr );
- (*k)._ptr = 0;
- (*k)._mid = 0;
- (*k)._page = 0;
- break;
- }
- if ( k == _shared.end() )
- _free_list.push_back(_mem_entry);
- _key_lock.unlock();
- _mem_lock.unlock();
- }
- //
- // cleanup
- //
- // Cleanup all memory allocated pages, by removing them
- // and marking them for destruction.
- //
- // dilemma:
- // Suppose there are some dirty pages around, a user hasn't
- // freed all memory, that he isn't using... what should I do?
- // should I destroy the page, or leave it? At this poiint,
- // I am destroying all references to shared memory.
- void shared_mem::cleanup()
- {
- struct shmid_ds sm;
- sto_iterator i;
- _key_lock.lock();
- _mem_lock.lock();
- for( i=_shared.begin();(i != _shared.end());i++ ) {
- if ( (*i)._ptr != 0 ) {
- #ifdef DEBUG
- cout.form("Mark page %p for removaln",(*i)._ptr);
- #endif
- // The page will be removed, when unattached... the
- // unattachement will occurr normally on exit.
- shmctl( (*i)._mid,IPC_RMID,&sm );
- }
- }
- // We have destroyed the pages, no re-entry is allowed.
- }