swoole では、セマフォは主に共有リソースを保護するために使用されるため、リソースには一度に 1 つのプロセスしか存在しません。セマフォの値が正の場合、テスト対象のスレッドをロックできることを意味します。セマフォ 値が 0 の場合、テストされたスレッドがスリープ キューに入り、ウェイクアップされるのを待つことを意味します。
このチュートリアルの動作環境: Windows10 システム、Swoole4 バージョン、DELL G3 パソコン
セマフォの使用は、主に共有リソースを保護するために使用され、リソースが一度に 1 つのプロセス (スレッド) のみによって所有されるようにします。セマフォの値が正の場合、それはアイドル状態であることを意味します。テスト対象のスレッドは、使用中にロックされる可能性があります。 0 の場合は占有されていることを意味し、テスト スレッドはスリープ キューに入り、ウェイクアップされるのを待ちます。
Linux は 2 種類のセマフォを提供します。(1) カーネル セマフォ。カーネル制御パスによって使用されます。
(2) ユーザー モードによって使用されます。処理セマフォは、POSIX セマフォと SYSTEM
V セマフォに分かれています。
POSIX セマフォは、名前付きセマフォと名前なしセマフォに分けられます。
名前付きセマフォ。その値はファイルに保存されるため、スレッドやプロセス間の同期に使用できます。値がメモリに保存される名前のない
セマフォ。
カーネル セマフォカーネル セマフォの構成
カーネル セマフォはスピン ロックに似ています。ロックが閉じられると、カーネル制御パスの続行を許可しません。ただし、
カーネル制御パスがカーネル セマフォ ロックによって保護されているビジー リソースを取得しようとすると、対応するプロセスは一時停止されます。リソースが解放された場合にのみ、プロセスが再び実行可能になります。
スリープ可能な関数のみがカーネル セマフォを取得でき、割り込みハンドラーも遅延可能関数も内部
コア セマフォを使用できません。
カーネル セマフォは struct セマフォ タイプのオブジェクトです。上記のルーチン
#include <pthread.h> #include <semaphore.h> #include <sys/types.h> #include <stdio.h> #include <unistd.h> int number; // 被保护的全局变量 sem_t sem_id; void* thread_one_fun(void *arg) { sem_wait(&sem_id); printf("thread_one have the semaphore\n"); number++; printf("number = %d\n",number); sem_post(&sem_id); } void* thread_two_fun(void *arg) { sem_wait(&sem_id); printf("thread_two have the semaphore \n"); number--; printf("number = %d\n",number); sem_post(&sem_id); } int main(int argc,char *argv[]) { number = 1; pthread_t id1, id2; sem_init(&sem_id, 0, 1); pthread_create(&id1,NULL,thread_one_fun, NULL); pthread_create(&id2,NULL,thread_two_fun, NULL); pthread_join(id1,NULL); pthread_join(id2,NULL); printf("main,,,\n"); return 0; }
では、どのスレッドが最初にセマフォ リソースを適用するかはランダムです。特定のシーケンスが必要な場合は、2 つのセマフォを使用してそれを実現できます。たとえば、次のルーチンでは、スレッド 1 が最初に実行を終了し、次にスレッド 2 が終了するまで
を継続します。
int number; // 被保护的全局变量 sem_t sem_id1, sem_id2; void* thread_one_fun(void *arg) { sem_wait(&sem_id1); printf(“thread_one have the semaphore\n”); number++; printf(“number = %d\n”,number); sem_post(&sem_id2); } void* thread_two_fun(void *arg) { sem_wait(&sem_id2); printf(“thread_two have the semaphore \n”); number–; printf(“number = %d\n”,number); sem_post(&sem_id1); } int main(int argc,char *argv[]) { number = 1; pthread_t id1, id2; sem_init(&sem_id1, 0, 1); // 空闲的 sem_init(&sem_id2, 0, 0); // 忙的 pthread_create(&id1,NULL,thread_one_fun, NULL); pthread_create(&id2,NULL,thread_two_fun, NULL); pthread_join(id1,NULL); pthread_join(id2,NULL); printf(“main,,,\n”); return 0; }
(b) 関連プロセス間の名前のないセマフォの同期
このプログラムには 2 つのプロセスがあり、一方が他方の子プロセスであるため、関連プロセスであると言われます (
フォーク
制作)。
元々 fork では、子プロセスは親プロセスのコード コピーのみを継承します。ミューテックスは親プロセスと子プロセスの 2 つの独立した変数である必要があります
。ただし、ミューテックスが初期化されると、pshared になります。 = 1 は、ミューテックスが共有メモリ領域にあることを示します。このとき、ミューテックスは親プロセスと子プロセスで共有される変数になります。この時点で、ミューテックスを使用して関連プロセスを同期できます。
rreeee2.名前付きセマフォ名前付きセマフォの特徴は、セマフォの値をファイルに保存することです。
これにより、非常に幅広い用途があることがわかります。スレッド、関連プロセス、さらには無関係の
プロセスにも使用できます。 (a) 名前付きセマフォをプロセス間で共有できる理由
名前付きセマフォの値はファイルに格納されているため、子プロセスはそれを関連プロセスに継承します。親プロセスのファイル記述子を継承すると、子プロセスが継承したファイル記述子は親プロセスと同じファイルを指します。もちろん、ファイルに保存された名前付きセマフォ値は共有されます。
(b) 名前付きセマフォに関する関数の説明
名前付きセマフォを使用する場合、sem_wait 関数および sem_post 関数は、名前なしセマフォと共有されます。
違いは、名前付きセマフォが sem_init ではなく sem_open を使用することです。さらに、最後に名前付きセマフォをファイルのように閉じる必要があります。
(1) 既存の名前付きセマフォを開くか、名前付きセマフォを作成して初期化します。 1 回の呼び出しで、セマフォの作成、初期化、権限の設定が完了します。
sem_t *sem_open(const char *name, int oflag, mode_t mode, int value);
name はファイルのパス名です;
Oflag には O_CREAT またはO_CREAT| EXCL には 2 つの値があります。
mode_t は新しいセマフォのアクセス権を制御し、
Value はセマフォの初期化値を指定します。
注:
Linux では、sem は /dev/shm に
作成されるため、ここでの名前を /tmp/aaa.sem の形式で記述することはできません。ディレクトリダウン。名前は「/mysem」または「mysem」と記述すると、作成されるファイルは「/dev/shm/sem.mysem」となりますので、パスは記述しないでください。また、「/tmp/mysem」などは決して書かないでください。
oflag = O_CREAT の場合、name で指定されたセマフォが存在しない場合はセマフォが作成され、次の
mode パラメーターと value パラメーターが有効である必要があります。名前で指定されたセマフォがすでに存在する場合は、セマフォを直接開き、
、モードと値のパラメーターを無視します。
oflag = O_CREAT|O_EXCL の場合、名前で指定されたセマフォがすでに存在する場合、関数は直接
error を返します。
(2) セマフォを使用したら、それを破棄することが重要です。
在做这个之前,要确定所有对这个有名信号量的引用都已经通过sem_close()函数
关闭了,然后只需在退出或是退出处理函数中调用sem_unlink()去删除系统中的信号量,
注意如果有任何的处理器或是线程引用这个信号量,sem_unlink()函数不会起到任何的作
用。
也就是说,必须是最后一个使用该信号量的进程来执行sem_unlick才有效。因为每个
信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把
name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。
(c)有名信号量在无相关进程间的同步
前面已经说过,有名信号量是位于共享内存区的,那么它要保护的资源也必须是位于
共享内存区,只有这样才能被无相关的进程所共享。
在下面这个例子中,服务进程和客户进程都使用shmget和shmat来获取得一块共享内
存资源。然后利用有名信号量来对这块共享内存资源进行互斥保护。
File1: server.c #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <semaphore.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define SHMSZ 27 char SEM_NAME[]= "vik"; int main() { char ch; int shmid; key_t key; char *shm,*s; sem_t *mutex; //name the shared memory segment key = 1000; //create & initialize semaphore mutex = sem_open(SEM_NAME,O_CREAT,0644,1); if(mutex == SEM_FAILED) { perror("unable to create semaphore"); sem_unlink(SEM_NAME); exit(-1); } //create the shared memory segment with this key shmid = shmget(key,SHMSZ,IPC_CREAT|0666); if(shmid<0) { perror("failure in shmget"); exit(-1); } //attach this segment to virtual memory shm = shmat(shmid,NULL,0); //start writing into memory s = shm; for(ch='A';ch<='Z';ch++) { sem_wait(mutex); *s++ = ch; sem_post(mutex); } //the below loop could be replaced by binary semaphore while(*shm != '*') { sleep(1); } sem_close(mutex); sem_unlink(SEM_NAME); shmctl(shmid, IPC_RMID, 0); exit(0); } <u>File 2: client.c</u> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <semaphore.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define SHMSZ 27 char SEM_NAME[]= "vik"; int main() { char ch; int shmid; key_t key; char *shm,*s; sem_t *mutex; //name the shared memory segment key = 1000; //create & initialize existing semaphore mutex = sem_open(SEM_NAME,0,0644,0); if(mutex == SEM_FAILED) { perror("reader:unable to execute semaphore"); sem_close(mutex); exit(-1); } //create the shared memory segment with this key shmid = shmget(key,SHMSZ,0666); if(shmid<0) { perror("reader:failure in shmget"); exit(-1); } //attach this segment to virtual memory shm = shmat(shmid,NULL,0); //start reading s = shm; for(s=shm;*s!=NULL;s++) { sem_wait(mutex); putchar(*s); sem_post(mutex); } //once done signal exiting of reader:This can be replaced by another semaphore *shm = '*'; sem_close(mutex); shmctl(shmid, IPC_RMID, 0); exit(0); }
SYSTEM V信号量
这是信号量值的集合,而不是单个信号量。相关的信号量操作函数由
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> static int nsems; static int semflg; static int semid; int errno=0; union semun { int val; struct semid_ds *buf; unsigned short *array; }arg; int main() { struct sembuf sops[2]; //要用到两个信号量,所以要定义两个操作数组 int rslt; unsigned short argarray[80]; arg.array = argarray; semid = semget(IPC_PRIVATE, 2, 0666); if(semid < 0 ) { printf("semget failed. errno: %d\n", errno); exit(0); } //获取0th信号量的原始值 rslt = semctl(semid, 0, GETVAL); printf("val = %d\n",rslt); //初始化0th信号量,然后再读取,检查初始化有没有成功 arg.val = 1; // 同一时间只允许一个占有者 semctl(semid, 0, SETVAL, arg); rslt = semctl(semid, 0, GETVAL); printf("val = %d\n",rslt); sops[0].sem_num = 0; sops[0].sem_op = -1; sops[0].sem_flg = 0; sops[1].sem_num = 1; sops[1].sem_op = 1; sops[1].sem_flg = 0; rslt=semop(semid, sops, 1); //申请0th信号量,尝试锁定 if (rslt < 0 ) { printf("semop failed. errno: %d\n", errno); exit(0); } //可以在这里对资源进行锁定 sops[0].sem_op = 1; semop(semid, sops, 1); //释放0th信号量 rslt = semctl(semid, 0, GETVAL); printf("val = %d\n",rslt); rslt=semctl(semid, 0, GETALL, arg); if (rslt < 0) { printf("semctl failed. errno: %d\n", errno); exit(0); } printf("val1:%d val2: %d\n",(unsigned int)argarray[0],(unsigned int)argarray[1]); if(semctl(semid, 1, IPC_RMID) == -1) { Perror(“semctl failure while clearing reason”); } return(0); }
推荐学习: swoole教程
以上がswooleでのセマフォの使用法は何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。