Linux環境プロセス間通信-信号量

44277 ワード

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