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