プロセス間通信のPOSIX信号量

11529 ワード

POSIX信号量インタフェースは、XSI信号量インタフェースのいくつかの不足点を解決することを意図している.
  • POSIX信号量インタフェースは、XSI信号量インタフェースに比べて、より高性能な実装を可能にする.
  • POSIX信号量インタフェースは簡単で使いやすい:信号量セットがなく、一部のインタフェースは私たちがよく知っているファイルシステムの操作を模倣している.
  • POSIX信号量削除時の処理がより合理的である.XSI信号量が削除されると、この信号量識別子を使用する動作がエラーで返され、errnoがEIDRMに設定されます.一方、POSIX信号量については、信号量の最後の参照が解放されるまで動作を正常に実行し続けることができる.

  • POSIX信号量には、有名と無名の2つの形式があります.それらの違いは、どのように作成され、破棄されるかであり、他の面ではまったく同じである.無名の信号量はメモリにのみ存在し、メモリにアクセスできるプロセスがメモリの信号量を使用できることを規定する.これは、無名信号量は、(1)同じプロセスからの各スレッド(2)異なるプロセスからの各スレッドのみで使用できるが、これらのプロセスは同じメモリ範囲を自分のアドレス空間にマッピングしていることを意味する.逆に,有名信号量は名前でアクセスするので,どのプロセスからのスレッドも,その有名信号量の名前が分かればアクセスできる.
    呼び出しsem_Open関数は、新しい有名信号量を作成したり、既存の有名信号量を開いたりすることができます.
    #include <semaphore.h>
    
    sem_t *sem_open(const char *name, int oflag, ... /* mode_t mode, unsigned int value */ );
    
       :              ,      SEM_FAILED

    既存の有名な信号量を使用する場合は、信号量名とoflag(oflagは0)の2つのパラメータを指定するだけです.oflagをO_に設定CREATフラグの場合、指定された信号量が存在しない場合は、有名な信号量を新規作成します.指定した信号量が既に存在する場合は、使用をオンにし、他の追加操作は発生しません.
    O_を指定するとCREATフラグは、modeとvalueの2つのパラメータを提供する必要があります.modeは、誰がこの信号量にアクセスできるかを指定するために使用されます.ファイルを開くときに使用する権限ビットの値を取ることができます(http://www.cnblogs.com/nufangrensheng/p/3502097.htmlの表4-5参照).最終的に信号量に付与されるアクセス権は、呼び出し元ファイルがシールドワードを作成することによって変更される(http://www.cnblogs.com/nufangrensheng/p/3502328.html).しかしながら、通常は読み書き権限のみが有用であることに注意するが、インタフェースは、既存の信号量を開くときに開くモード(mode)を指定することを許さない.実装は、通常、信号量を読み書きで開く.
    valueパラメータは、信号量の初期値を指定するために使用されます.0-SEM_VALUE_MAX.
    新しい信号量を作成していることを確認するには、oflagパラメータをO_に設定します.CREAT|O_EXCL.信号量がすでに存在するとsem_Open呼び出しに失敗しました.
    移植性を向上させるためには,信号量の名前を選択する際に,一定の約束に従わなければならない.
  • の名前の最初の文字は、スラッシュ(/)でなければなりません.
  • 頭文字を除き、名前に他のスラッシュ(/)を含めることはできません.
  • 名前の最長長さは実装によって定義され、超えてはならない.POSIX_NAME_MAX文字.

  • sem_Open関数は、信号量を操作する他の関数で使用できる信号量ポインタを返します.使用が完了したらsem_を呼び出しますclose関数は信号量に関連するリソースを解放する.
    #include <semaphore.h>
    
    int sem_close(sem_t *sem);
    
       :      0,    -1

    プロセスがsem_を呼び出していない場合closeが終了すると、カーネルはプロセスが開いているすべての信号量を自動的に閉じます.これは、信号量値の状態に影響を与えることはありません.例えば、信号量の値を増やした場合、終了後もこの値は変わりません.同様にsem_を呼び出したらcloseでは、信号量値も影響を受けません.POSIX信号量メカニズムではXSI信号量のSEM_と同様ではないUNDOマーク.
    呼び出しsem_unlink関数は、有名な信号量を破棄します.
    #include <semaphore.h>
    
    int sem_unlink(const char *name);
    
       :      0,     -1

    sem_unlink関数は、信号量の名前を削除します.現在開いている信号量の参照がない場合は、破棄します.そうでなければ、最後に開いた参照が閉じられるまで破棄が延期されます.
    XSI信号量とは異なり、POSIX信号量の値に1を加算または減算するしかありません.信号量値を1減少させることは、カウント信号量に関連するリソースを1つの2値信号量にロックまたは要求することと同様である.
    なお、POSIX信号量は、信号量タイプを区別していない.2値信号量を使用するかカウント信号量を使用するかは、信号を初期化して使用する場合によって異なります.信号量値が0と1しか取れない場合、それは2値信号量である.2値信号量値が1の場合、ロックされていないと言います.値が0の場合は、ロックされています.
    呼び出しsem_waitまたはsem_trywait関数は、信号量を要求する(信号量値を1減算する).
    #include <semaphore.h>
    
    int sem_trywait(sem_t *sem);
    
    int sem_wait(sem_t *sem);
    
           :      0,     -1

    信号量カウントが0の場合、sem_が呼び出されるwait関数は、ブロックされます.信号量カウントが1減少したり、1つの信号で中断されたりするまでsem_wait関数が返されます.sem_を使用できますtrywait関数は、ブロックを回避します.sem_を呼び出すとtrywait関数の場合、信号量カウントが0の場合、sem_trywaitは-1を返し、errnoをEAGAINに設定します.
    3つ目の方法は、限られた時間をブロックすることができます.この場合、sem_を使用します.timedwait関数.
    #include <semaphore.h>
    
    #include <time.h>
    
    
    
    int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict tsptr);
    
       :      0,     -1

    tsptrパラメータは、待機する絶対時間を指定します.信号量がすぐに1を減らすことができれば、タイムアウトしても構いません.過去の時間を指定しても、信号量を1に減らそうとする操作は成功します.タイムアウトまで信号量カウントを1減算できない場合sem_timedwait関数は-1を返し、errnoをETIMEDOUTに設定します.
    呼び出しsem_post関数は信号量値に1を加算する.これは、カウント信号量に関連するリソースを1つの2値信号量に対してロック解除または解放することと同様である.
    #include <semaphore.h>
    
    int sem_post(sem_t *sem);
    
       :      0,     -1

    sem_を呼び出すとpostの場合、sem_が呼び出された場合waitまたはsem_timedwaitによってブロックされたプロセスは、起動されsem_postプラス1の信号量カウントは直後にsem_waitまたはsem_timedwaitは1を減らします.
    単一プロセスでPOSIX信号量を使用したい場合は、無名信号量を使用するとより簡単になります.無名信号量は作成と破棄が変更されただけで、他は完全に有名な信号量と同じです.sem_を呼び出しますInit関数は、無名の信号量を作成します.
    #include <semaphore.h>
    
    int sem_init(sem_t *sem, int pshared, unsigned int value);
    
       :      0,    -1

    psharedパラメータは、マルチプロセス間で無名の信号量を使用するかどうかを示します.複数のプロセス間で使用する場合は、psharedを0以外の値に設定します.valueパラメータは信号量の初期値を指定します.
    またsem_を宣言する必要がありますtタイプの変数をsem_に転送しますinitは、変数を初期化するために使用されます.この無名の信号量を2つのプロセス間で使用する場合は、semパラメータが2つのプロセスが共有するメモリ範囲内を指すことを確認する必要があります.
    sem_を呼び出すことができますdestroy関数は、使用済みの無名信号量を破棄する.
    #include <semaphore.h>
    
    int sem_destroy(sem_t *sem);
    
       :      0,     -1

    呼び出しsem_destroyの後、semを再使用しない限り、semをパラメータとして信号量関数を呼び出すことはできません.Initはsemを初期化します.
    sem_を呼び出すことができますgetvalue関数を使用して信号量値を取得します.
    #include <semaphore.h>
    
    int sem_getvalue(sem_t *sem, int *restrict valp);
    
       :      0,     -1

    sem_getvalueの実行に成功し、信号量の値はvalpが指す整数変数に格納されます.しかし、私たちが読んだばかりの信号量の値が変わる可能性があることに注意してください(私たちはいつでもこの信号量の値を使用する可能性があります).追加の同期メカニズムを取らなければsem_getvalue関数はデバッグにのみ使用されます.
    ≪インスタンス|Instance|emdw≫
    http://www.cnblogs.com/nufangrensheng/p/3523623.htmlの表12-4を思い出すと、Single UNIX Specificationは、1つのスレッドがnormalタイプのmutexをロックしたときに、別のスレッドがこのmutexをロックしようとしたときに何が起こるかを定義していない(表12-4の「占有しないときにロックを解除する」欄に対応).ただしerror-checkingタイプとrecursiveタイプのmutexでは、この場合エラーが発生します.二値信号量は反発量(mutex)のように使用できるので、信号量を使用して独自のロック原語(primitive)を作成して反発を提供することができます.
    独自のロックを作成するとします.1つのスレッドにロックされ、別のスレッドにロックされます.ロック構造は次のとおりです.
    struct slock {
    
        sem_t    *semp;
    
        char     name[_POSIX_NAME_MAX];
    
    };

    プログラムリスト15〜35は、信号量に基づく反発原語の実装を示す.
    プログラムリスト15-35 POSIX信号量を用いた反発
    #include "slock.h"
    
    #include <stdlib.h>
    
    #include <stdio.h>
    
    #include <unistd.h>
    
    #include <error.h>
    
    
    
    struct slock *
    
    s_alloc()
    
    {
    
        struct slock *sp;
    
        static int    cnt;
    
        
    
        if((sp = malloc(sizeof(struct slock))) == NULL)
    
            return(NULL);
    
        do
    
        {
    
            snprintf(sp->name, sizeof(sp->name), "/%ld.%d", (long)getpid(),
    
                cnt++);
    
            sp->semp =sem_open(sp->name, O_CREAT|O_EXCL, S_IRWXU, 1);
    
        }
    
        while((sp->semp == SEM_FAILED) && (errno == EEXIST));
    
        if(sp->semp == SEM_FAILED)
    
        {
    
            free(sp);
    
            return(NULL);
    
        }
    
        sem_unlink(sp->name);
    
        return(sp);
    
    }
    
    
    
    void
    
    s_free(struct slock *sp)
    
    {
    
        sem_close(sp->semp);
    
        free(sp);
    
    }
    
    
    
    int 
    
    s_lock(struct slock *sp)
    
    {
    
        return(sem_wait(sp->semp));
    
    }
    
    
    
    int
    
    s_trylock(struct slock *sp)
    
    {
    
        return(sem_trywait(sp->semp));
    
    }
    
    
    
    int
    
    s_unlock(struct slock *sp)
    
    {
    
        return(sem_post(sp->semp));
    
    }

    プロセスIDとカウントcounterに基づいて名前を作成します.競合する2つのスレッドがs_を同時に呼び出す場合、反発量を使用してcounterを保護する必要はありません.allocには同じ名前が割り当てられていますが、O_を使用します.EXCLフラグ呼び出しsem_Openでは1つの呼び出しが成功するだけで、もう1つはEEXISTエラーで返されます.この場合、もう一度呼び出すだけでいいです.信号量をオンにすると破棄されます.これにより、他のプロセスはアクセスできなくなり、プロセス終了時のクリーンアップ作業が簡素化されます.
    このブログの内容は『UNIX環境高度プログラミング』(第3版)から抜粋し、個人学習記録としてのみ使用されている.本書についてはhttp://www.apuebook.com/を参照してください.