在swoole中,號誌主要用來保護共用資源,使得資源在一個時刻只有一個程序;號誌的值為正的時候,說明所測試的執行緒可以鎖定而使用,號誌的值若為0,則說明測試的執行緒要進入睡眠佇列中,等待被喚醒。
本教學操作環境:Windows10系統、Swoole4版、DELL G3電腦
號誌的使用主要是用來保護共用資源,使得資源在一個時刻只有一個程序(執行緒)
所擁有。號誌的值為正的時候,說明它空閒。所測試的執行緒可以鎖定而使用它。若為0,說明它被佔用,測試的執行緒要進入睡眠佇列中,等待被喚醒。
Linux提供兩種號誌:
(1) 核心號誌,由核心控制路徑使用
(2) 使用者態程序使用的號誌,這種號誌又分為POSIX號誌和SYSTEM
V號誌。
POSIX號誌又分為有名號誌和無名號誌。
有名號誌,其值儲存在檔案中, 所以它可以用於執行緒也可以用於程序間的同步。無名
號誌,其值儲存在記憶體中。
核心號誌
核心號誌的構成
核心號誌類似於自旋鎖,因為當鎖關閉著時,它不允許核心控制路徑繼續進行。然而,
當核心控制路徑試圖獲取核心號誌鎖保護的忙資源時,相應的程序就被掛起。只有在資源被釋放時,程序才再次變為可執行。
只有可以睡眠的函數才能獲取核心號誌;中斷處理程式和可延遲函數都不能使用內
核號誌。
核心號誌是struct semaphore型別的物件,它在
#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
產生)的。
本來對於fork來說,子程序只繼承了父程序的程式碼副本,mutex理應在父子程序
中是相互獨立的兩個變數,但由於在初始化mutex的時候,由pshared = 1指
定了mutex處於共用記憶體區域,所以此時mutex變成了父子程序共用的一個變
量。此時,mutex就可以用來同步相關程序了。
#include <semaphore.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> 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) < 0) // { perror("semaphore initilization"); exit(0); } if (fork() == 0) { /* child process*/ for (i = 0; i < nloop; i++) { sem_wait(&mutex); printf("child: %d\n", (*ptr)++); sem_post(&mutex); } exit(0); } /* back to parent process */ for (i = 0; i < nloop; i++) { sem_wait(&mutex); printf("parent: %d\n", (*ptr)++); sem_post(&mutex); } exit(0); }
2.有名號誌
有名號誌的特點是把號誌的值儲存在檔案中。
這決定了它的用途非常廣:既可以用於執行緒,也可以用於相關程序間,甚至是不相關
程序。
(a)有名號誌能在程序間共用的原因
由於有名號誌的值是儲存在檔案中的,所以對於相關程序來說,子程序是繼承了父
程序的檔案描述符,那麼子程序所繼承的檔案描述符所指向的檔案是和父程序一樣的,當
然檔案裡面儲存的有名號誌值就共用了。
(b)有名號誌相關函數說明
有名號誌在使用的時候,和無名號誌共用sem_wait和sem_post函數。
區別是有名號誌使用sem_open代替sem_init,另外在結束的時候要像關閉檔案
一樣去關閉這個有名號誌。
(1)開啟一個已存在的有名號誌,或建立並初始化一個有名號誌。一個單一的呼叫就完
成了號誌的建立、初始化和許可權的設定。
sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);
name是檔案的路徑名;
Oflag 有O_CREAT或O_CREAT|EXCL兩個取值;
mode_t控制新的號誌的存取許可權;
Value指定號誌的初始化值。
注意:
這裡的name不能寫成/tmp/aaa.sem這樣的格式,因為在linux下,sem都是建立
在/dev/shm目錄下。你可以將name寫成「/mysem」或「mysem」,建立出來的檔案都
是「/dev/shm/sem.mysem」,千萬不要寫路徑。也千萬不要寫「/tmp/mysem」之類的。
當oflag = O_CREAT時,若name指定的號誌不存在時,則會建立一個,而且後
面的mode和value引數必須有效。若name指定的號誌已存在,則直接開啟該號誌,
同時忽略mode和value引數。
當oflag = O_CREAT|O_EXCL時,若name指定的號誌已存在,該函數會直接返
回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中號誌的用法是什麼的詳細內容,更多請關注TW511.COM其它相關文章!