在swoole中,信號(hào)量主要用來(lái)保護(hù)共享資源,使得資源在一個(gè)時(shí)刻只有一個(gè)進(jìn)程;信號(hào)量的值為正的時(shí)候,說(shuō)明所測(cè)試的線程可以鎖定而使用,信號(hào)量的值若為0,則說(shuō)明測(cè)試的線程要進(jìn)入睡眠隊(duì)列中,等待被喚醒。
本教程操作環(huán)境:Windows10系統(tǒng)、Swoole4版、DELL G3電腦
swoole中信號(hào)量的用法是什么
信號(hào)量的使用主要是用來(lái)保護(hù)共享資源,使得資源在一個(gè)時(shí)刻只有一個(gè)進(jìn)程(線程)
所擁有。信號(hào)量的值為正的時(shí)候,說(shuō)明它空閑。所測(cè)試的線程可以鎖定而使用它。若為0,說(shuō)明它被占用,測(cè)試的線程要進(jìn)入睡眠隊(duì)列中,等待被喚醒。
linux提供兩種信號(hào)量:
(1)?內(nèi)核信號(hào)量,由內(nèi)核控制路徑使用
(2)?用戶態(tài)進(jìn)程使用的信號(hào)量,這種信號(hào)量又分為POSIX信號(hào)量和SYSTEM
V信號(hào)量。
POSIX信號(hào)量又分為有名信號(hào)量和無(wú)名信號(hào)量。
有名信號(hào)量,其值保存在文件中,?所以它可以用于線程也可以用于進(jìn)程間的同步。無(wú)名
信號(hào)量,其值保存在內(nèi)存中。
內(nèi)核信號(hào)量
內(nèi)核信號(hào)量的構(gòu)成
內(nèi)核信號(hào)量類似于自旋鎖,因?yàn)楫?dāng)鎖關(guān)閉著時(shí),它不允許內(nèi)核控制路徑繼續(xù)進(jìn)行。然而,
當(dāng)內(nèi)核控制路徑試圖獲取內(nèi)核信號(hào)量鎖保護(hù)的忙資源時(shí),相應(yīng)的進(jìn)程就被掛起。只有在資源被釋放時(shí),進(jìn)程才再次變?yōu)榭蛇\(yùn)行。
只有可以睡眠的函數(shù)才能獲取內(nèi)核信號(hào)量;中斷處理程序和可延遲函數(shù)都不能使用內(nèi)
核信號(hào)量。
內(nèi)核信號(hào)量是Struct?semaphore類型的對(duì)象,它在
#include?<pthread.h> #include?<semaphore.h> #include?<sys> #include?<stdio.h> #include?<unistd.h> int?number;?//?被保護(hù)的全局變量 sem_t?sem_id; void*?thread_one_fun(void?*arg) { sem_wait(&sem_id); printf("thread_one?have?the?semaphoren"); number++; printf("number?=?%dn",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?=?%dn",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; }</unistd.h></stdio.h></sys></semaphore.h></pthread.h>
上面的例程,到底哪個(gè)線程先申請(qǐng)到信號(hào)量資源,這是隨機(jī)的。如果想要某個(gè)特定的順
序的話,可以用2個(gè)信號(hào)量來(lái)實(shí)現(xiàn)。例如下面的例程是線程1先執(zhí)行完,然后線程2才繼
續(xù)執(zhí)行,直至結(jié)束。
int?number;?//?被保護(hù)的全局變量 sem_t?sem_id1,?sem_id2; void*?thread_one_fun(void?*arg) { sem_wait(&sem_id1); printf(“thread_one?have?the?semaphoren”); number++; printf(“number?=?%dn”,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?=?%dn”,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)無(wú)名信號(hào)量在相關(guān)進(jìn)程間的同步
說(shuō)是相關(guān)進(jìn)程,是因?yàn)楸境绦蛑泄灿?個(gè)進(jìn)程,其中一個(gè)是另外一個(gè)的子進(jìn)程(由
fork
產(chǎn)生)的。
本來(lái)對(duì)于fork來(lái)說(shuō),子進(jìn)程只繼承了父進(jìn)程的代碼副本,mutex理應(yīng)在父子進(jìn)程
中是相互獨(dú)立的兩個(gè)變量,但由于在初始化mutex的時(shí)候,由pshared?=?1指
定了mutex處于共享內(nèi)存區(qū)域,所以此時(shí)mutex變成了父子進(jìn)程共享的一個(gè)變
量。此時(shí),mutex就可以用來(lái)同步相關(guān)進(jìn)程了。
#include?<semaphore.h> #include?<stdio.h> #include?<errno.h> #include?<stdlib.h> #include?<unistd.h> #include?<sys> #include?<sys> #include?<fcntl.h> #include?<sys> int?main(int?argc,?char?**argv) { int?fd,?i,count=0,nloop=10,zero=0,*ptr; sem_t?mutex; //open?a?file?and?map?it?into?memory fd?=?open("log.txt",O_RDWR|O_CREAT,S_IRWXU); write(fd,&zero,sizeof(int)); ptr?=?mmap(?NULL,sizeof(int),PROT_READ?| PROT_WRITE,MAP_SHARED,fd,0?); close(fd); /*?create,?initialize?semaphore?*/ if(?sem_init(&mutex,1,1)?<p><strong>2.有名信號(hào)量</strong></p> <p>有名信號(hào)量的特點(diǎn)是把信號(hào)量的值保存在文件中。</p> <p>這決定了它的用途非常廣:既可以用于線程,也可以用于相關(guān)進(jìn)程間,甚至是不相關(guān)</p> <p>進(jìn)程。</p> <p>(a)有名信號(hào)量能在進(jìn)程間共享的原因</p> <p>由于有名信號(hào)量的值是保存在文件中的,所以對(duì)于相關(guān)進(jìn)程來(lái)說(shuō),子進(jìn)程是繼承了父</p> <p>進(jìn)程的文件描述符,那么子進(jìn)程所繼承的文件描述符所指向的文件是和父進(jìn)程一樣的,當(dāng)</p> <p>然文件里面保存的有名信號(hào)量值就共享了。</p> <p>(b)有名信號(hào)量相關(guān)函數(shù)說(shuō)明</p> <p>有名信號(hào)量在使用的時(shí)候,和無(wú)名信號(hào)量共享sem_wait和sem_post函數(shù)。</p> <p>區(qū)別是有名信號(hào)量使用sem_open代替sem_init,另外在結(jié)束的時(shí)候要像關(guān)閉文件</p> <p>一樣去關(guān)閉這個(gè)有名信號(hào)量。</p> <p>(1)打開一個(gè)已存在的有名信號(hào)量,或創(chuàng)建并初始化一個(gè)有名信號(hào)量。一個(gè)單一的調(diào)用就完</p> <p>成了信號(hào)量的創(chuàng)建、初始化和權(quán)限的設(shè)置。</p> <p>sem_t?*sem_open(const?char?*name,?int?oflag,?mode_t?mode?,?int?value);</p> <p>name是文件的路徑名;</p> <p>Oflag?有O_CREAT或O_CREAT|EXCL兩個(gè)取值;</p> <p>mode_t控制新的信號(hào)量的訪問(wèn)權(quán)限;</p> <p>Value指定信號(hào)量的初始化值。</p> <p>注意:</p> <p>這里的name不能寫成/tmp/aaa.sem這樣的格式,因?yàn)樵趌inux下,sem都是創(chuàng)建</p> <p>在/dev/shm目錄下。你可以將name寫成“/mysem”或“mysem”,創(chuàng)建出來(lái)的文件都</p> <p>是“/dev/shm/sem.mysem”,千萬(wàn)不要寫路徑。也千萬(wàn)不要寫“/tmp/mysem”之類的。</p> <p>當(dāng)oflag?=?O_CREAT時(shí),若name指定的信號(hào)量不存在時(shí),則會(huì)創(chuàng)建一個(gè),而且后</p> <p>面的mode和value參數(shù)必須有效。若name指定的信號(hào)量已存在,則直接打開該信號(hào)量,</p> <p>同時(shí)忽略mode和value參數(shù)。</p> <p>當(dāng)oflag?=?O_CREAT|O_EXCL時(shí),若name指定的信號(hào)量已存在,該函數(shù)會(huì)直接返</p> <p>回Error。</p> <p>(2)?一旦你使用了信號(hào)量,銷毀它們就變得很重要。</p> <p>在做這個(gè)之前,要確定所有對(duì)這個(gè)有名信號(hào)量的引用都已經(jīng)通過(guò)sem_close()函數(shù)</p> <p>關(guān)閉了,然后只需在退出或是退出處理函數(shù)中調(diào)用sem_unlink()去刪除系統(tǒng)中的信號(hào)量,</p> <p>注意如果有任何的處理器或是線程引用這個(gè)信號(hào)量,sem_unlink()函數(shù)不會(huì)起到任何的作</p> <p>用。</p> <p>也就是說(shuō),必須是最后一個(gè)使用該信號(hào)量的進(jìn)程來(lái)執(zhí)行sem_unlick才有效。因?yàn)槊總€(gè)</p> <p>信號(hào)燈有一個(gè)引用計(jì)數(shù)器記錄當(dāng)前的打開次數(shù),sem_unlink必須等待這個(gè)數(shù)為0時(shí)才能把</p> <p>name所指的信號(hào)燈從文件系統(tǒng)中刪除。也就是要等待最后一個(gè)sem_close發(fā)生。</p> <p>(c)有名信號(hào)量在無(wú)相關(guān)進(jìn)程間的同步</p> <p>前面已經(jīng)說(shuō)過(guò),有名信號(hào)量是位于共享內(nèi)存區(qū)的,那么它要保護(hù)的資源也必須是位于</p> <p>共享內(nèi)存區(qū),只有這樣才能被無(wú)相關(guān)的進(jìn)程所共享。</p> <p>在下面這個(gè)例子中,服務(wù)進(jìn)程和客戶進(jìn)程都使用shmget和shmat來(lái)獲取得一塊共享內(nèi)</p> <p>存資源。然后利用有名信號(hào)量來(lái)對(duì)這塊共享內(nèi)存資源進(jìn)行互斥保護(hù)。</p> <pre class="brush:php;toolbar:false">File1:?server.c #include?<sys> #include?<sys> #include?<sys> #include?<stdio.h> #include?<semaphore.h> #include?<sys> #include?<sys> #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(shmidFile?2:?client.c #include?<sys> #include?<sys> #include?<sys> #include?<stdio.h> #include?<semaphore.h> #include?<sys> #include?<sys> #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<p>SYSTEM?V信號(hào)量</p> <p>這是信號(hào)量值的集合,而不是單個(gè)信號(hào)量。相關(guān)的信號(hào)量操作函數(shù)由</p> <pre class="brush:php;toolbar:false">#include?<sys> #include?<sys> #include?<sys> #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];?//要用到兩個(gè)信號(hào)量,所以要定義兩個(gè)操作數(shù)組 int?rslt; unsigned?short?argarray[80]; arg.array?=?argarray; semid?=?semget(IPC_PRIVATE,?2,?0666); if(semid?<p>推薦學(xué)習(xí): <a href="https://www.php.cn/swoole/" target="_blank">swoole教程</a></p></stdio.h></sys></sys></sys>