您好,登錄后才能下訂單哦!
本篇內容主要講解“POSIX有名信號量與無名信號量有哪些區別”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“POSIX有名信號量與無名信號量有哪些區別”吧!
有名信號量和無名信號量的差異在于創建和銷毀的形式上,但是其他工作一樣。
無名信號量只能存在于內存中,要求使用信號量的進程必須能訪問信號量所在的這一塊內存,所以無名信號量只能應用在同一進程內的線程之間(共享進程的內存),或者不同進程中已經映射相同內存內容到它們的地址空間中的線程(即信號量所在內存被通信的進程共享)。意思是說無名信號量只能通過共享內存訪問。
相反,有名信號量可以通過名字訪問,因此可以被任何知道它們名字的進程中的線程使用。
單個進程中使用 POSIX 信號量時,無名信號量更簡單。多個進程間使用 POSIX 信號量時,有名信號量更簡單。
無論是有名信號量還是無名信號量,都可以通過以下函數進行信號量值操作。
weit 為信號量值減一操作,總共有三個函數,函數原型如下:
#include <semaphore.h> int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); Link with -pthread.這一句表示 gcc 編譯時,要加 -pthread.返回值:若成功,返回 0 ;若出錯,返回-1 復制代碼
其中,第一個函數的作用是,若 sem 小于 0 ,則線程阻塞于信號量 sem ,直到 sem 大于 0 ;否則信號量值減1。
第二個函數作用與第一個相同,只是此函數不阻塞線程,如果 sem 小于 0,直接返回一個錯誤(錯誤設置為 EAGAIN )。
第三個函數作用也與第一個相同,第二個參數表示阻塞時間,如果 sem 小于 0 ,則會阻塞,參數指定阻塞時間長度。 abs_timeout 指向一個結構體,這個結構體由從 1970-01-01 00:00:00 +0000 (www.taobao-wd.com) 開始的秒數和納秒數構成。結構體定義如下:
struct timespec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds [0 .. 999999999] */ }; 復制代碼
如果指定的阻塞時間到了,但是 sem 仍然小于 0 ,則會返回一個錯誤 (錯誤設置為 ETIMEDOUT )。
post 為信號量值加一操作,函數原型如下:
#include <semaphore.h> int sem_post(sem_t *sem); Link with -pthread.返回值:若成功,返回 0 ;若出錯,返回-1 復制代碼
創建
有名信號量創建可以調用 sem_open 函數,函數說明如下:
#include <semaphore.h> sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); Link with -pthread.返回值:若成功,返回指向信號量的指針;若出錯,返回SEM_FALLED 復制代碼
其中第一種函數是當使用已有的有名信號量時調用該函數,flag 參數設為 0 。
如果要調用第二種函數,flag 參數應設為 O_CREAT ,如果有名信號量不存在,則會創建一個新的,如果存在,則會被使用并且不會再初始化。
當我們使用 O_CREAT 標志時,需要提供兩個額外的參數:
mode 參數指定誰可以訪問信號量,即權限組,mode 的取值和打開文件的權限位相同,比如 0666 表示 所有用戶可讀寫 。因為只有讀和寫訪問要緊,所以實現經常為讀和寫打開信號量。
value 指定信號量的初始值,取值范圍為 0~SEM_VALUE_MAX 。
如果信號量存在,則調用第二個函數會忽略后面兩個參數(即 mode 和 value )。
釋放
當完成信號量操作以后,可以調用 sem_close 函數來釋放任何信號量的資源。函數說明如下:
#include <semaphore.h> int sem_close(sem_t *sem); Link with -pthread.返回值:若成功,返回 0 ;若出錯,返回-1 復制代碼
如果進程沒有調用該函數便退出了,內核會自動關閉任何打開的信號量。無論是調用該函數還是內核自動關閉,都不會改變釋放之前的信號量值。
銷毀
可以使用 sem_unlink 函數銷毀一個有名信號量。函數說明如下:
#include <semaphore.h> int sem_unlink(const char *name); Link with -pthread.返回值:若成功,返回 0 ;若出錯,返回-1 復制代碼
sem_unlink 函數會刪除信號量的名字。如果沒有打開的信號量引用,則該信號量會被銷毀,否則,銷毀會推遲到最后一個打開的引用關閉時才進行。
例子
例如,管道通信中,如果父進程使用 fork()創建兩個子進程1和2,子進程1,2按順序向管道寫一段文字,最后父進程從管道將子進程寫入的內容讀出來,要保證進程執行的先后順序,可以用有名信號量來解決。
#include<unistd.h> #include<signal.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<sys/wait.h> #include<semaphore.h> #include<sys/sem.h> #include <sys/stat.h> #include<fcntl.h> int main(){ int pid1,pid2; sem_t *resource1; sem_t *resource2; int Cpid1,Cpid2=-1; int fd[2];//0為讀出段,1為寫入端 char outpipe1[100],inpipe[200],outpipe2[100]; pipe(fd);//建立一個無名管道 pid1 = fork(); if(pid1<0){ printf("error in the first fork!"); }else if(pid1==0){//子進程1 resource1=sem_open("name_sem1",O_CREAT,0666,0); Cpid1 = getpid(); close(fd[0]);//關掉讀出端 lockf(fd[1],1,0);//上鎖,則鎖定從當前偏移量到文件結尾的區域 sprintf(outpipe1,"Child process 1 is sending a message!"); write(fd[1],outpipe1,strlen(outpipe2)); lockf(fd[1],0,0);//解鎖 sem_post(resource1); sem_close(resource1); exit(0); }else{ pid2 = fork(); if(pid2<0){ printf("error in the second fork!\n"); }else if(pid2==0){ resource1=sem_open("name_sem1",O_CREAT,0666,0); resource2=sem_open("name_sem2",O_CREAT,0666,0); Cpid2 = getpid(); sem_wait(resource1); close(fd[0]); lockf(fd[1],1,0); sprintf(outpipe2,"Child process 2 is sending a message!"); write(fd[1],outpipe2,strlen(outpipe2)); lockf(fd[1],0,0);//解鎖 sem_post(resource2); sem_close(resource1); sem_close(resource2); exit(0); } if(pid1 > 0 && pid2 >0){ resource2=sem_open("name_sem2",O_CREAT,0666,0); sem_wait(resource2); waitpid(pid1,NULL,0); waitpid(pid2,NULL,0); close(fd[1]);//關掉寫端 read(fd[0],inpipe,200); printf("%s\n",inpipe); sem_close(resource2); exit(0); } sem_unlink("name_sem1"); sem_unlink("name_sem2"); } return 0; } 復制代碼
創建
無名信號量可以通過 sem_init 函數創建,函數說明如下:
#include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); Link with -pthread.返回值:若成功,返回 0 ;若出錯,返回-1 復制代碼
pshared 參數指示該信號量是被一個進程的多個線程共享還是被多個進程共享。
如果 www.sinorichmake.com 的值為 0 ,那么信號量將被單進程中的多線程共享,并且應該位于某個地址,該地址對所有線程均可見(例如,全局變量或變量在堆上動態分配)。
如果 pshared 非零,那么信號量將在進程之間共享,并且信號量應該位于共享內存區域。
銷毀
如果無名信號量使用完成,可以調用 sem_destory 函數銷毀該信號量。函數說明如下:
#include <semaphore.h> int sem_destroy(sem_t *sem); Link with -pthread.返回值:若成功,返回 0 ;若出錯,返回-1 復制代碼
注意:
銷毀其他進程或線程當前被阻塞的信號量會產生未定義的行為。
使用已銷毀的信號量會產生未定義的結果,除非使用 sem_init 重新初始化信號量。
一個無名信號量應該在它所在的內存被釋放前用 sem_destroy 銷毀。如果不這樣做,可能會導致某些實現出現資源泄漏。
例子
使用無名信號量實現有名信號量中的例子:
#include<unistd.h> #include<signal.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<sys/wait.h> #include<semaphore.h> #include<sys/sem.h> #include <sys/stat.h> #include <sys/shm.h> #include<fcntl.h> int main(){ int pid1,pid2; int Cpid1,Cpid2=-1; int fd[2];//0為讀出段,1為寫入端 char outpipe1[100],inpipe[200],outpipe2[100]; void *shm = NULL; sem_t *shared; int shmid = shmget((key_t)(1234), sizeof(sem_t *), 0666 | IPC_CREAT);//創建一個共享內存,返回一個標識符 if(shmid == -1){ perror("shmat :"); exit(0); } shm = shmat(shmid, 0, 0);//返回指向共享內存第一個字節的指針 shared = (sem_t *)shm; sem_init(shared, 1, 0);//初始化共享內存信號量值為0 pipe(fd);//建立一個無名管道 pid1 = fork(); if(pid1<0){ printf("error in the first fork!"); }else if(pid1==0){//子進程1 Cpid1 = getpid(); close(fd[0]);//關掉讀出端 lockf(fd[1],1,0);//上鎖,則鎖定從當前偏移量到文件結尾的區域 sprintf(outpipe1,"Child process 1 is sending a message!"); write(fd[1],outpipe1,strlen(outpipe1)); lockf(fd[1],0,0);//解鎖 sem_post(shared); exit(0); }else{ pid2 = fork(); if(pid2<0){ printf("error in the second fork!\n"); }else if(pid2==0){ sem_wait(shared); Cpid2 = getpid(); close(fd[0]); lockf(fd[1],1,0); sprintf(outpipe2,"Child process 2 is sending a message!"); write(fd[1],outpipe2,strlen(outpipe2)); lockf(fd[1],0,0);//解鎖 exit(0); } if(pid1 > 0 && pid2 >0){ waitpid(pid2,NULL,0);//同步,保證子進程先寫父進程再讀 close(fd[1]);//關掉寫端 read(fd[0],inpipe,200); printf("%s\n",inpipe); exit(0); } } return 0; }
到此,相信大家對“POSIX有名信號量與無名信號量有哪些區別”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。