共享內存允許兩個或多個進程進程共享同一塊內存(這塊內存會映射到各個進程自己獨立的地址空間) 從而使得這些進程可以相互通信。 在GNU/Linux中所有的進程都有唯一的虛擬地址空間,而共享內存應用編程接口API允許一個進程使 用公共內存區段。但是對內存的共享訪問其復雜度也相應增加。共享內存的優點是簡易性。 使用消息隊列時,一個進程要向隊列中寫入消息,這要引起從用戶地址空間向內核地址空間的一次復制, 同樣一個進程進行消息讀取時也要進行一次復制。共享內存的優點是完全省去了這些操作。 共享內存會映射到進程的虛擬地址空間,進程對其可以直接訪問,避免了數據的復制過程。 因此,共享內存是GNU/Linux現在可用的最快速的IPC機制。 進程退出時會自動和已經掛接的共享內存區段分離,但是仍建議當進程不再使用共享區段時 調用shmdt來卸載區段。 注意,當一個進程分支出父進程和子進程時,父進程先前創建的所有共享內存區段都會被子進程繼承。 如果區段已經做了刪除標記(在前面以IPC——RMID指令調用shmctl),而當前掛接數已經變為0, 這個區段就會被移除。
shmget() 創建一個新的共享內存區段
取得一個共享內存區段的描述符
shmctl() 取得一個共享內存區段的信息
為一個共享內存區段設置特定的信息
移除一個共享內存區段
shmat() 掛接一個共享內存區段
shmdt() 於一個共享內存區段的分離
//創建一個共享內存區段,並顯示其相關信息,然後刪除該內存共享區
#include <stdio.h>
#include <unistd.h> //getpagesize( )
#include <sys/ipc.h>
#include <sys/shm.h>
#define MY_SHM_ID 67483
int main( )
{
//獲得系統中頁面的大小
printf( "page size=%d/n",getpagesize( ) );
//創建一個共享內存區段
int shmid,ret;
shmid=shmget( MY_SHM_ID,4096,0666|IPC_CREAT );
//創建了一個4KB大小共享內存區段。指定的大小必須是當前系統架構
//中頁面大小的整數倍
if( shmid>0 )
printf( "Create a shared memory segment %d/n",shmid );
//獲得一個內存區段的信息
struct shmid_ds shmds;
//shmid=shmget( MY_SHM_ID,0,0 );//示例怎樣獲得一個共享內存的標識符
ret=shmctl( shmid,IPC_STAT,&shmds );
if( ret==0 )
{
printf( "Size of memory segment is %d/n",shmds.shm_segsz );
printf( "Numbre of attaches %d/n",( int )shmds.shm_nattch );
}
else
{
printf( "shmctl( ) call failed/n" );
}
//刪除該共享內存區
ret=shmctl( shmid,IPC_RMID,0 );
if( ret==0 )
printf( "Shared memory removed /n" );
else
printf( "Shared memory remove failed /n" );
return 0;
}
//共享內存區段的掛載,脫離和使用
//理解共享內存區段就是一塊大內存
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <errno.h>
#define MY_SHM_ID 67483
int main( )
{
//共享內存區段的掛載和脫離
int shmid,ret;
void* mem;
shmid=shmget( MY_SHM_ID,0,0 );
if( shmid>=0 )
{
mem=shmat( shmid,( const void* )0,0 );
//shmat()返回進程地址空間中指向區段的指針
if( ( int )mem!=-1 )
{
printf( "Shared memory was attached in our address space at %p/n",mem );
//向共享區段內存寫入數據
strcpy( ( char* )mem,"This is a test string./n" );
printf( "%s/n",(char*)mem );
//脫離共享內存區段
ret=shmdt( mem );
if( ret==0 )
printf( "Successfully detached memory /n" );
else
printf( "Memory detached failed %d/n",errno );
}
else
printf( "shmat( ) failed/n" );
}
else
printf( "shared memory segment not found/n" );
return 0;
}
/*內存共享區段與旗語和消息隊列不同,一個區段可以被鎖定。
被鎖定的區段不允許被交換出內存。這樣做的優勢在於,與其
把內存區段交換到文件系統,在某個應用程序調用時再交換回內存,
不如讓它一直處於內存中,且對多個應用程序可見。從提升性能的角度
來看,很重要的。
*/
int shmid;
//...
shmid=shmget( MY_SHM_ID,0,0 );
ret=shmctl( shmid,SHM_LOCK,0 );
if( ret==0 )
printf( "Locked!/n" );
////////////////////////////////////////////////////////////////////////
/*使用旗語協調共享內存的例子
使用和編譯命令
gcc -Wall test.c -o test
./test create
./test use a &
./test use b &
./test read &
./test remove
*/
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define MY_SHM_ID 34325
#define MY_SEM_ID 23234
#define MAX_STRING 200
typedef struct
{
int semID;
int counter;
char string[ MAX_STRING+1 ];
}MY_BLOCK_T;
int main(int argc,char** argv)
{
int shmid,ret,i;
MY_BLOCK_T* block;
struct sembuf sb;
char user;
//make sure there is a command
if( argc>=2 )
{
//create the shared memory segment and init it
//with the semaphore
if( !strncmp(argv[ 1 ],"create",6) )
{
//create the shared memory segment and semaphore
printf( "Creating the shared memory/n" );
shmid=shmget( MY_SHM_ID,sizeof( MY_BLOCK_T ),( IPC_CREAT|0666 ) );
block=( MY_BLOCK_T* )shmat( shmid,( const void* )0,0 );
block->counter=0;
//create the semaphore and init
block->semID=semget(MY_SEM_ID,1,( IPC_CREAT|0666 ));
sb.sem_num=0;
sb.sem_op=1;
sb.sem_flg=0;
semop( block->semID,&sb,1 );
//now detach the segment
shmdt( ( void* )block );
printf( "Create the shared memory and semaphore successuflly/n" );
}
else if( !strncmp(argv[ 1 ],"use",3) )
{
/*use the segment*/
//must specify also a letter to write to the buffer
if( argc<3 ) exit( -1 );
user=( char )argv[ 2 ][ 0 ];
//grab the segment
shmid=shmget( MY_SHM_ID,0,0 );
block=( MY_BLOCK_T* )shmat( shmid,( const void* )0,0 );
/*##########重點就是使用旗語對共享區的訪問###########*/
for( i=0;i<100;++i )
{
sleep( 1 ); //設置成1s就會看到 a/b交替出現,為0則a和b連續出現
//grab the semaphore
sb.sem_num=0;
sb.sem_op=-1;
sb.sem_flg=0;
if( semop( block->semID,&sb,1 )!=-1 )
{
//write the letter to the segment buffer
//this is our CRITICAL SECTION
block->string[ block->counter++ ]=user;
sb.sem_num=0;
sb.sem_op=1;
sb.sem_flg=0;
if( semop( block->semID,&sb,1 )==-1 )
printf( "Failed to release the semaphore/n" );
}
else
printf( "Failed to acquire the semaphore/n" );
}
//do some clear work
ret=shmdt(( void*)block);
}
else if( !strncmp(argv[ 1 ],"read",4) )
{
//here we will read the buffer in the shared segment
shmid=shmget( MY_SHM_ID,0,0 );
if( shmid!=-1 )
{
block=( MY_BLOCK_T* )shmat( shmid,( const void* )0,0 );
block->string[ block->counter+1 ]=0;
printf( "%s/n",block->string );
printf( "Length=%d/n",block->counter );
ret=shmdt( ( void*)block );
}
else
printf( "Unable to read segment/n" );
}
else if( !strncmp(argv[ 1 ],"remove",6) )
{
shmid=shmget( MY_SHM_ID,0,0 );
if( shmid>=0 )
{
block=( MY_BLOCK_T* )shmat( shmid,( const void* )0,0 );
//remove the semaphore
ret=semctl( block->semID,0,IPC_RMID );
if( ret==0 )
printf( "Successfully remove the semaphore /n" );
//remove the shared segment
ret=shmctl( shmid,IPC_RMID,0 );
if( ret==0 )
printf( "Successfully remove the segment /n" );
}
}
else
printf( "Unkonw command/n" );
}
return 0;
}