プロセス間通信--信号量+共有メモリ

45412 ワード

信号量はカウンタであり、プロセスとスレッドの同期問題、特に臨界リソースへのアクセスの同期によく使われる.
一度の信号量を取得する動作は、信号量を一度減らすことであり、一度の信号量を解放する動作は、信号量を一度加えることである.
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