プロセス間通信--信号量+共有メモリ
45412 ワード
信号量はカウンタであり、プロセスとスレッドの同期問題、特に臨界リソースへのアクセスの同期によく使われる.
一度の信号量を取得する動作は、信号量を一度減らすことであり、一度の信号量を解放する動作は、信号量を一度加えることである.
Linuxカーネルは各信号セットにsemid_を提供しています.dsデータ構造.この構造は以下のように定義されています.
int semget(keymut key,int nsems,int semflug);
関数が成功したら信号セットの識別子を返します.失敗したら-1に戻ります.関数の最初のパラメータはftk()からキー値を得ます.第二のパラメータnsemsは、作成する信号セットに含まれる信号の個数を指定します.信号セットを開くだけで、nsemsを0に設定すればいいです.三つ目のパラメータsemflugは操作フラグです.以下の値を取ることができます.
IPC_CREAT:semgetを呼び出すと、この値は他の信号セットのkeyと比較されます.同じKeyが存在するなら、信号セットが既に存在していることを示しています.このとき、信号セットの識別子に戻ります.そうでないと、新しい信号セットを作成し、その識別子に戻ります.
IPC_EXCL:同マクロ須とIPC_CREATと一緒に使います.IPC_を使用するCREAT 124 IPC_EXCLの場合、信号セットが既に存在していることが判明したらエラーを返し、エラーコードはEEXISTとなります.
(2)信号量の動作、信号量の値および対応するリソースの使用状況は、その値が0より大きい場合、現在利用可能なリソースの数を表し、その値が0より小さい場合、その絶対値は、そのリソースを使用するプロセスの個数を待つことを示しています.信号量の値はPV操作のみで変更できます.Linuxでは、関数semopを呼び出してPV操作が行われます.この関数はファイル/sys/sem.hで定義されています.プロトタイプは以下の通りです.
int semop(int semid、struct sembof*sops、size nsops)
パラメータsemidは信号セットの識別子である.パラメータsopsは動作する構造体配列のヘッダアドレスを指します.パラメータnsopsは、動作する信号の個数を示します.semop関数の呼び出しが成功したら0に戻ります.さもなければ-1に戻ります.
sopsパラメータの定義は以下の通りです.
struct semuf{
ushort 信号セットのインデックス
ショート?ト semuop; //操作の種類
ショート?ト semufg;//操作フラグ
}
表1
semopの値と意味
取得範囲
操作の意義
semop>0
信号にsem_を加えますop.プロセス解放制御のリソースを表します.
semop=0
IPC_が設置されていない場合NOWAITは、信号値が0になるまで睡眠に入ります.そうでないと、プロセスは眠りません.直接にEAGAINに戻ります.
semop<0
信号にsem_を加えますopの値は、IPC_が設定されていない場合NOWAITは資源が使えるまで睡眠に入ります.そうでないと直接EAGAINに戻ります.
(3)信号量の制御は、信号量を使用する場合、信号セットの削除、カーネル保守の信号セットのデータ構造semid_など、いくつかの制御操作が必要になります.dsは設定を行い、信号集中信号値などを取得する.semctlで操作できます.(sys/sem.h)
int semctl(int semid,int semnum,int cmd,...)
関数では、パラメータsemidは信号セットの識別子であり、パラメータsemnumは特定の信号を識別する.cmdは制御動作の種類を指定します.最後の「…」は、関数のパラメータがオプションであり、3番目のパラメータcmdに依存しています.これは、共用体変数semunを介して操作するパラメータを選択します.semunは、Linux/sem.h:
1)val:SETVAL操作タイプのみに使用し、ある信号を設定する値はvalに等しい.
2)buf:IPC_に用いるSTATとIPC_SET、アクセスsemid_ds構造
3)array:SETALLとGETALLの操作に使います.
4)buf:IPC_を制御するためINFO提供のキャッシュ
cmdのマクロの意味は以下の通りです.
IPC_SET:信号量の属性を設定します.
IPC_RNID:semid指定の信号セットを削除します.
GETPID:最後のsemop操作を実行するプロセスIDを返します.
GETVAL:信号セットsemnum指定信号の値を返します.
GETALL:信号セットに使用される信号の値を返します.
GETNCNT:リソース待ちプロセスの数を返します.
GETZCNT:完全な空きリソースを待っているプロセスの数を返します.
SETVAL:信号集中semnumで指定された信号の値を設定します.
SETALL:信号セット用信号の値を設定します.
共有メモリ:
共有メモリとは、他のプロセスにアクセスできるメモリを割り当てます.各共有メモリセグメントはカーネルの中で一つの内部構造shmid_を維持しています.ds:(linux/shm.h)
int shmget(keymglf)
パラメータkeyとshmflugはsemflugのパラメータを参照することができます.sizeはバイト単位で指定されたメモリのサイズです.
(2)共有メモリの操作:(linux/shm.h)
void*shmat(int shmid、const void*shmaddr、int shmflag)
共有メモリを使用する前に、shmat関数でプロセスのアドレス空間に追加する必要があります.shmat呼び出しが成功すると共有メモリエリアへのポインタが戻ります.このポインタを使って共有メモリにアクセスできます.もし失敗したら-1に戻ります.
shmatパラメータshmidはshmgetの戻り値です.パラメータshmflagはアクセス許可フラグです.パラメータshmaddrは共有メモリの追加点です.パラメータshmaddrの異なる取得状況は以下の通りである.
1)空きの場合は、カーネルから空きメモリを選択します.そうでなければ、リターンアドレスは、スケジューラがshmflugにSHM_をセットしたかどうかによって決まります.RND値が指定されていない場合、共有メモリエリアはshmaddrによって指定されたアドレスに付加されます.そうでなければ、追加アドレスはshmaddrです.
共有メモリの使用が終了すると、共有メモリとのリンクが関数で切断されます.
(sys/shm.h):
int shmdt(const void*shmaddr)パラメータshmaddrはshmatの戻り値で、この関数の呼び出しが成功したら0を返します.そうでなければ-1を返します.
(3)メモリの制御を共有します.(sys/shm.h):
int shmctl(int shmid、int cmd、struct shmid buf)
パラメータshmidは共有メモリ領域の識別子であり、shmget関数の戻り値である.bufはshmid_を指す.ds構造体の指針;cmdは操作フラグビットで、以下の3つの操作をサポートします.
1)IPC_RMID:shmidが指す共有メモリエリアをシステムから削除します.
2)IPC_SET:共有メモリのshmid_を設定します.ds構造
3)IPC_STAT:共有メモリエリアのshmid_を読み込みます.ds機関は、bufが指すアドレスに格納する.
ここでは、簡単な読者と書き手を例にして、信号量を学習し、メモリを共有します.
プログラム全体の設計規定は以下の通りです.
1、まず書き込み者に信号量を取得させて、臨界エリアを書き込みます.これは臨界エリアです.共有メモリです.完了後に信号量を放出します.
2、読者が信号量を取得したら、臨界領域のデータを読み、データを読み出し終わったら、信号量を解放する.
以上の読者と書き手はそれぞれ二つのプロセスです.コードは以下の通りです.
src/shm_write.c(作成者コード):
shm_mem.h:
一度の信号量を取得する動作は、信号量を一度減らすことであり、一度の信号量を解放する動作は、信号量を一度加えることである.
Linuxカーネルは各信号セットにsemid_を提供しています.dsデータ構造.この構造は以下のように定義されています.
/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct semid_ds {
struct ipc_perm sem_perm; /* */
__kernel_time_t sem_otime; /* */
__kernel_time_t sem_ctime; /* */
struct sem * sem_base; /* */
struct sem_queue * sem_pending; /* */
struct sem_queue * * sem_pending_last; /* */
struct sem_undo * undo; /* */
unsigned short sem_nsems; /* */
} ;
(1)Linuxではシステム関数を使って信号セットを作成して開けます.この関数はヘッダファイルsys/sem.hで定義されています.関数のプロトタイプは以下の通りです.int semget(keymut key,int nsems,int semflug);
関数が成功したら信号セットの識別子を返します.失敗したら-1に戻ります.関数の最初のパラメータはftk()からキー値を得ます.第二のパラメータnsemsは、作成する信号セットに含まれる信号の個数を指定します.信号セットを開くだけで、nsemsを0に設定すればいいです.三つ目のパラメータsemflugは操作フラグです.以下の値を取ることができます.
IPC_CREAT:semgetを呼び出すと、この値は他の信号セットのkeyと比較されます.同じKeyが存在するなら、信号セットが既に存在していることを示しています.このとき、信号セットの識別子に戻ります.そうでないと、新しい信号セットを作成し、その識別子に戻ります.
IPC_EXCL:同マクロ須とIPC_CREATと一緒に使います.IPC_を使用するCREAT 124 IPC_EXCLの場合、信号セットが既に存在していることが判明したらエラーを返し、エラーコードはEEXISTとなります.
(2)信号量の動作、信号量の値および対応するリソースの使用状況は、その値が0より大きい場合、現在利用可能なリソースの数を表し、その値が0より小さい場合、その絶対値は、そのリソースを使用するプロセスの個数を待つことを示しています.信号量の値はPV操作のみで変更できます.Linuxでは、関数semopを呼び出してPV操作が行われます.この関数はファイル/sys/sem.hで定義されています.プロトタイプは以下の通りです.
int semop(int semid、struct sembof*sops、size nsops)
パラメータsemidは信号セットの識別子である.パラメータsopsは動作する構造体配列のヘッダアドレスを指します.パラメータnsopsは、動作する信号の個数を示します.semop関数の呼び出しが成功したら0に戻ります.さもなければ-1に戻ります.
sopsパラメータの定義は以下の通りです.
struct semuf{
ushort 信号セットのインデックス
ショート?ト semuop; //操作の種類
ショート?ト semufg;//操作フラグ
}
表1
semopの値と意味
取得範囲
操作の意義
semop>0
信号にsem_を加えますop.プロセス解放制御のリソースを表します.
semop=0
IPC_が設置されていない場合NOWAITは、信号値が0になるまで睡眠に入ります.そうでないと、プロセスは眠りません.直接にEAGAINに戻ります.
semop<0
信号にsem_を加えますopの値は、IPC_が設定されていない場合NOWAITは資源が使えるまで睡眠に入ります.そうでないと直接EAGAINに戻ります.
(3)信号量の制御は、信号量を使用する場合、信号セットの削除、カーネル保守の信号セットのデータ構造semid_など、いくつかの制御操作が必要になります.dsは設定を行い、信号集中信号値などを取得する.semctlで操作できます.(sys/sem.h)
int semctl(int semid,int semnum,int cmd,...)
関数では、パラメータsemidは信号セットの識別子であり、パラメータsemnumは特定の信号を識別する.cmdは制御動作の種類を指定します.最後の「…」は、関数のパラメータがオプションであり、3番目のパラメータcmdに依存しています.これは、共用体変数semunを介して操作するパラメータを選択します.semunは、Linux/sem.h:
/* arg for semctl system calls. */
union semun {
int val; /* value for SETVAL */
struct semid_ds * buf; /* buffer for IPC_STAT & IPC_SET */
unsigned short * array; /* array for GETALL & SETALL */
struct seminfo * __buf; /* buffer for IPC_INFO */
void * __pad;
} ;
上記の各フィールドの意味は以下の通りです.1)val:SETVAL操作タイプのみに使用し、ある信号を設定する値はvalに等しい.
2)buf:IPC_に用いるSTATとIPC_SET、アクセスsemid_ds構造
3)array:SETALLとGETALLの操作に使います.
4)buf:IPC_を制御するためINFO提供のキャッシュ
cmdのマクロの意味は以下の通りです.
IPC_SET:信号量の属性を設定します.
IPC_RNID:semid指定の信号セットを削除します.
GETPID:最後のsemop操作を実行するプロセスIDを返します.
GETVAL:信号セットsemnum指定信号の値を返します.
GETALL:信号セットに使用される信号の値を返します.
GETNCNT:リソース待ちプロセスの数を返します.
GETZCNT:完全な空きリソースを待っているプロセスの数を返します.
SETVAL:信号集中semnumで指定された信号の値を設定します.
SETALL:信号セット用信号の値を設定します.
共有メモリ:
共有メモリとは、他のプロセスにアクセスできるメモリを割り当てます.各共有メモリセグメントはカーネルの中で一つの内部構造shmid_を維持しています.ds:(linux/shm.h)
/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct shmid_ds {
struct ipc_perm shm_perm; /* */
int shm_segsz; /* , */
__kernel_time_t shm_atime; /* */
__kernel_time_t shm_dtime; /* */
__kernel_time_t shm_ctime; /* */
__kernel_ipc_pid_t shm_cpid; /* ID */
__kernel_ipc_pid_t shm_lpid; /* ID */
unsigned short shm_nattch; /* */
unsigned short shm_unused; /* compatibility */
void * shm_unused2; /* ditto - used by DIPC */
void * shm_unused3; /* unused */
} ;
(1)共有メモリの作成:(linux/shm.h)int shmget(keymglf)
パラメータkeyとshmflugはsemflugのパラメータを参照することができます.sizeはバイト単位で指定されたメモリのサイズです.
(2)共有メモリの操作:(linux/shm.h)
void*shmat(int shmid、const void*shmaddr、int shmflag)
共有メモリを使用する前に、shmat関数でプロセスのアドレス空間に追加する必要があります.shmat呼び出しが成功すると共有メモリエリアへのポインタが戻ります.このポインタを使って共有メモリにアクセスできます.もし失敗したら-1に戻ります.
shmatパラメータshmidはshmgetの戻り値です.パラメータshmflagはアクセス許可フラグです.パラメータshmaddrは共有メモリの追加点です.パラメータshmaddrの異なる取得状況は以下の通りである.
1)空きの場合は、カーネルから空きメモリを選択します.そうでなければ、リターンアドレスは、スケジューラがshmflugにSHM_をセットしたかどうかによって決まります.RND値が指定されていない場合、共有メモリエリアはshmaddrによって指定されたアドレスに付加されます.そうでなければ、追加アドレスはshmaddrです.
共有メモリの使用が終了すると、共有メモリとのリンクが関数で切断されます.
(sys/shm.h):
int shmdt(const void*shmaddr)パラメータshmaddrはshmatの戻り値で、この関数の呼び出しが成功したら0を返します.そうでなければ-1を返します.
(3)メモリの制御を共有します.(sys/shm.h):
int shmctl(int shmid、int cmd、struct shmid buf)
パラメータshmidは共有メモリ領域の識別子であり、shmget関数の戻り値である.bufはshmid_を指す.ds構造体の指針;cmdは操作フラグビットで、以下の3つの操作をサポートします.
1)IPC_RMID:shmidが指す共有メモリエリアをシステムから削除します.
2)IPC_SET:共有メモリのshmid_を設定します.ds構造
3)IPC_STAT:共有メモリエリアのshmid_を読み込みます.ds機関は、bufが指すアドレスに格納する.
ここでは、簡単な読者と書き手を例にして、信号量を学習し、メモリを共有します.
プログラム全体の設計規定は以下の通りです.
1、まず書き込み者に信号量を取得させて、臨界エリアを書き込みます.これは臨界エリアです.共有メモリです.完了後に信号量を放出します.
2、読者が信号量を取得したら、臨界領域のデータを読み、データを読み出し終わったら、信号量を解放する.
以上の読者と書き手はそれぞれ二つのプロセスです.コードは以下の通りです.
src/shm_write.c(作成者コード):
# include "shm_mem.h"
int main( int argc, char * * argv)
{
int semid, shmid;
char * shmaddr;
char write_str[ SHM_SIZE] ;
char * ret;
if ( ( shmid = creatshm( "." , 57, SHM_SIZE) ) = = - 1) //
return - 1;
/* */
if ( ( shmaddr = shmat( shmid, ( char * ) 0, 0) ) = = ( char * ) - 1) {
perror ( "attch shared memory error!/n" ) ;
exit ( 1) ;
}
if ( ( semid = creatsem( "./" , 39, 1, 1) ) = = - 1)//
return - 1;
while ( 1) {
wait_sem( semid, 0) ;//
sem_p( semid, 0) ; //
/*************** ***************************************************/
printf ( "write : " ) ;
ret = fgets ( write_str, 1024, stdin ) ;
if ( write_str[ 0] = = '#' ) // '#'
break ;
int len = strlen ( write_str) ;
write_str[ len] = '/0' ;
strcpy ( shmaddr, write_str) ;
・/****************************************************************************/
sem_v( semid, 0) ; //
usleep ( 1000) ; // .
}
sem_delete( semid) ; // semid
deleteshm( shmid) ; // shmid
return 0;
}
src/shm_read.c(読者コード):# include "shm_mem.h"
int main( int argc, char * * argv)
{
int semid, shmid;
char * shmaddr;
printf ( "What!!!!!!!!!!!/n" ) ;
if ( ( shmid = creatshm( "." , 57, SHM_SIZE) ) = = - 1)
return - 1;
if ( ( shmaddr = shmat( shmid, ( char * ) 0, 0) ) = = ( char * ) - 1) {
perror ( "attch shared memory error!/n" ) ;
exit ( 1) ;
}
if ( ( semid = opensem( "./" , 39) ) = = - 1)
return - 1;
printf ( "read start..................../n" ) ;
while ( 1) {
printf ( "read : " ) ;
wait_sem( semid, 0) ; //
if ( sem_p( semid, 0) = = - 1) 。 '#'
break ;
printf ( "%s" , shmaddr) ;
sem_v( semid, 0) ;
usleep ( 1000) ;
}
return 0;
}
shm_mem.h:
# include < stdio. h>
# include < stdlib. h>
# include < string . h>
# include < unistd. h>
# include < sys/ types. h>
# include < sys/ ipc. h>
# include < sys/ sem. h>
# include < sys/ shm. h>
# include < error . h>
# define SHM_SIZE 1024
union semun{
int val;
struct semid_ds * buf;
unsigned short * array;
struct seminfo * buf_info;
void * pad;
} ;
/* */
int creatsem( const char * pathname, int proj_id, int members, int init_val)
{
key_t msgkey;
int index, sid;
union semun semopts;
if ( ( msgkey = ftok( pathname, proj_id) ) = = - 1) {
perror ( "ftok error!/n" ) ;
return - 1;
}
if ( ( sid = semget( msgkey, members, IPC_CREAT| 0666) ) = = - 1) {
perror ( "semget call failed./n" ) ;
return - 1;
}
semopts. val = init_val;
for ( index = 0; index < members; index+ + ) {
semctl( sid, index, SETVAL, semopts) ;
}
return sid;
}
int opensem( const char * pathname, int proj_id)
{
key_t msgkey;
int sid;
if ( ( msgkey = ftok( pathname, proj_id) ) = = - 1) {
perror ( "ftok error!/n" ) ;
return - 1;
}
if ( ( sid = semget( msgkey, 0, 0666) ) = = - 1) {
perror ( "open semget call failed./n" ) ;
return - 1;
}
return sid;
}
/* p , */
int sem_p( int semid, int index)
{
//struct sembuf sbuf = {0, -1, SEM_UNDO};
struct sembuf sbuf = { 0, - 1, IPC_NOWAIT} ;
if ( index < 0) {
perror ( "index of array cannot equals a minus value!/n" ) ;
return - 1;
}
sbuf. sem_num = index;
if ( semop( semid, & sbuf, 1) = = - 1) {
perror ( "A wrong operation to semaphore occurred!/n" ) ;
return - 1;
}
return 0;
}
/* V , */
int sem_v( int semid, int index)
{
//struct sembuf sbuf = {0, 1, SEM_UNDO};
struct sembuf sbuf = { 0, 1, IPC_NOWAIT} ;
if ( index < 0) {
perror ( "index of array cannot equals a minus value!/n" ) ;
return - 1;
}
sbuf. sem_num = index;
if ( semop( semid, & sbuf, 1) = = - 1) {
perror ( "A wrong operation to semaphore occurred!/n" ) ;
return - 1;
}
return 0;
}
/* */
int sem_delete( int semid)
{
return ( semctl( semid, 0, IPC_RMID) ) ;
}
/* 1*/
int wait_sem( int semid, int index)
{
while ( semctl( semid, index, GETVAL, 0) = = 0)
{
//wait_num++;
usleep ( 500) ;
}
//printf("wait_num = %x/n", wait_num);
return 1;
}
/* */
int creatshm( char * pathname, int proj_id, size_t size)
{
key_t shmkey;
int sid;
if ( ( shmkey = ftok( pathname, proj_id) ) = = - 1) {
perror ( "ftok error!/n" ) ;
return - 1;
}
if ( ( sid = shmget( shmkey, size, IPC_CREAT| 0666) ) = = - 1) {
perror ( "shm call failed!/n" ) ;
return - 1;
}
return sid;
}
/* */
int deleteshm( int sid)
{
void * p = NULL ;
return ( shmctl( sid, IPC_RMID, p) ) ;
}
原文の回転:http://blogold.chinaunix.net/u3/94212/showart.php?id=2341244