上一章,講述了 SYSTEM V 信號(hào)量,主要運(yùn)行于進(jìn)程之間,本章主要介紹 POSIX 信號(hào)量:有名信號(hào)量、無(wú)名信號(hào)量。
POSIX 信號(hào)量
POSIX 信號(hào)量進(jìn)程是 3 種 IPC(Inter-Process Communication) 機(jī)制之一,3 種 IPC 機(jī)制源于 POSIX.1 的實(shí)時(shí)擴(kuò)展。Single UNIX Specification 將 3 種機(jī)制(消息隊(duì)列,信號(hào)量和共享存儲(chǔ))置于可選部分中。在 SUSv4 之前,POSIX 信號(hào)量接口已經(jīng)被包含在信號(hào)量選項(xiàng)中。在 SUSv4 中,這些接口被移至了基本規(guī)范,而消息隊(duì)列和共享存儲(chǔ)接口依然是可選的。
POSIX 信號(hào)量接口意在解決 XSI 信號(hào)量接口的幾個(gè)缺陷。
相比于 XSI 接口,POSIX 信號(hào)量接口考慮了更高性能的實(shí)現(xiàn)。
POSIX 信號(hào)量使用更簡(jiǎn)單:沒(méi)有信號(hào)量集,在熟悉的文件系統(tǒng)操作后一些接口被模式化了。盡管沒(méi)有要求一定要在文件系統(tǒng)中實(shí)現(xiàn),但是一些系統(tǒng)的確是這么實(shí)現(xiàn)的。
POSIX 信號(hào)量在刪除時(shí)表現(xiàn)更完美。回憶一下,當(dāng)一個(gè) XSI 信號(hào)量被刪除時(shí),使用這個(gè)信號(hào)量標(biāo)識(shí)符的操作會(huì)失敗,并將 errno 設(shè)置成 EIDRM。使用 POSIX 信號(hào)量時(shí),操作能繼續(xù)正常工作直到該信號(hào)量的最后一次引用被釋放。
分類(lèi)
POSIX 信號(hào)量是一個(gè) sem_t 類(lèi)型的變量,但 POSIX 有兩種信號(hào)量的實(shí)現(xiàn)機(jī)制:無(wú)名信號(hào)量和命名信號(hào)量。無(wú)名信號(hào)量只可以在共享內(nèi)存的情況下,比如實(shí)現(xiàn)進(jìn)程中各個(gè)線(xiàn)程之間的互斥和同步,因此無(wú)名信號(hào)量也被稱(chēng)作基于內(nèi)存的信號(hào)量;命名信號(hào)量通常用于不共享內(nèi)存的情況下,比如進(jìn)程間通信。
同時(shí),在創(chuàng)建信號(hào)量時(shí),根據(jù)信號(hào)量取值的不同,POSIX 信號(hào)量還可以分為:
二值信號(hào)量:信號(hào)量的值只有 0 和 1,這和互斥量很類(lèi)似,若資源被鎖住,信號(hào)量的值為 0,若資源可用,則信號(hào)量的值為 1;
計(jì)數(shù)信號(hào)量:信號(hào)量的值在 0 到一個(gè)大于 1 的限制值之間,該計(jì)數(shù)表示可用的資源的個(gè)數(shù)。
區(qū)別
有名信號(hào)量和無(wú)名信號(hào)量的差異在于創(chuàng)建和銷(xiāo)毀的形式上,但是其他工作一樣。
無(wú)名信號(hào)量只能存在于內(nèi)存中,要求使用信號(hào)量的進(jìn)程必須能訪(fǎng)問(wèn)信號(hào)量所在的這一塊內(nèi)存,所以無(wú)名信號(hào)量只能應(yīng)用在同一進(jìn)程內(nèi)的線(xiàn)程之間(共享進(jìn)程的內(nèi)存),或者不同進(jìn)程中已經(jīng)映射相同內(nèi)存內(nèi)容到它們的地址空間中的線(xiàn)程(即信號(hào)量所在內(nèi)存被通信的進(jìn)程共享)。意思是說(shuō)無(wú)名信號(hào)量只能通過(guò)共享內(nèi)存訪(fǎng)問(wèn)。
相反,有名信號(hào)量可以通過(guò)名字訪(fǎng)問(wèn),因此可以被任何知道它們名字的進(jìn)程中的線(xiàn)程使用。
單個(gè)進(jìn)程中使用 POSIX 信號(hào)量時(shí),無(wú)名信號(hào)量更簡(jiǎn)單。多個(gè)進(jìn)程間使用 POSIX 信號(hào)量時(shí),有名信號(hào)量更簡(jiǎn)單。
聯(lián)系
無(wú)論是有名信號(hào)量還是無(wú)名信號(hào)量,都可以通過(guò)以下函數(shù)進(jìn)行信號(hào)量值操作。
wait(P)
wait 為信號(hào)量值減一操作,總共有三個(gè)函數(shù),函數(shù)原型如下:
#include intsem_wait(sem_t*sem); intsem_trywait(sem_t*sem); intsem_timedwait(sem_t*sem,conststructtimespec*abs_timeout); Linkwith-pthread. 這一句表示gcc編譯時(shí),要加-pthread. 返回值: 若成功,返回0;若出錯(cuò),返回 -1
sem_wait 的作用是,若 sem 小于 0 ,則線(xiàn)程阻塞于信號(hào)量 sem ,直到 sem 大于 0 ;否則信號(hào)量值減 1。
sem_trywait 作用與 sem_wait 相同,只是此函數(shù)不阻塞線(xiàn)程,如果 sem 小于 0,直接返回一個(gè)錯(cuò)誤(錯(cuò)誤設(shè)置為 EAGAIN )。
sem_timedwait 作用也與 sem_wait 相同,第二個(gè)參數(shù)表示阻塞時(shí)間,如果 sem 小于 0 ,則會(huì)阻塞,參數(shù)指定阻塞時(shí)間長(zhǎng)度。abs_timeout 指向一個(gè)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體由從 1970-01-01 00:00:00 +0000 (UTC) 開(kāi)始的秒數(shù)和納秒數(shù)構(gòu)成。
結(jié)構(gòu)體定義如下:
structtimespec{ time_ttv_sec;/*Seconds*/ longtv_nsec;/*Nanoseconds[0..999999999]*/ };
如果指定的阻塞時(shí)間到了,但是 sem 仍然小于 0 ,則會(huì)返回一個(gè)錯(cuò)誤 (錯(cuò)誤設(shè)置為 ETIMEDOUT )。
post(V)
post 為信號(hào)量值加一操作,函數(shù)原型如下:
#include intsem_post(sem_t*sem); Linkwith-pthread. 返回值: 若成功,返回0;若出錯(cuò),返回 -1
無(wú)名信號(hào)量
接口函數(shù)
信號(hào)量的函數(shù)都以 sem_ 開(kāi)頭,線(xiàn)程中使用的基本信號(hào)函數(shù)有 4 個(gè),他們都聲明在頭文件 semaphore.h 中,該頭文件定義了用于信號(hào)量操作的 sem_t 類(lèi)型:
sem_init
該函數(shù)用于創(chuàng)建信號(hào)量,原型如下:
intsem_init(sem_t*sem,intpshared,unsignedintvalue);
功能:該函數(shù)初始化由 sem 指向的信號(hào)對(duì)象,設(shè)置它的共享選項(xiàng),并給它一個(gè)初始的整數(shù)值。pshared 控制信號(hào)量的類(lèi)型,如果其值為 0,就表示信號(hào)量是當(dāng)前進(jìn)程的局部信號(hào)量,否則信號(hào)量就可以在多個(gè)進(jìn)程間共享,value 為 sem 的初始值。返回值:該函數(shù)調(diào)用成功返回 0,失敗返回 -1。
sem_destroy
該函數(shù)用于對(duì)用完的信號(hào)量進(jìn)行清理,其原型如下:
intsem_destroy(sem_t*sem);
返回值:
成功返回 0,失敗返回 -1。
sem_getvalue 函數(shù)
該函數(shù)返回當(dāng)前信號(hào)量的值,通過(guò) restrict 輸出參數(shù)返回。如果當(dāng)前信號(hào)量已經(jīng)上鎖(即同步對(duì)象不可用),那么返回值為 0,或?yàn)樨?fù)數(shù),其絕對(duì)值就是等待該信號(hào)量解鎖的線(xiàn)程數(shù)。
intsem_getvalue(sem_t*restrict,int*restrict);
使用實(shí)例
【實(shí)例 1】:
#include #include #include #include #include #include #include #include sem_tsem; #definehandle_error(msg)do{/ perror(msg);/ exit(EXIT_FAILURE);/ }while(0) staticvoidhandler(intsig){ write(STDOUT_FILENO,"sem_post()fromhandler/n",24); if(sem_post(&sem)==-1) { write(STDERR_FILENO,"sem_post()failed/n",18); _exit(EXIT_FAILURE); }} intmain(intargc,char*argv[]){ ints; structtimespects; structsigactionsa; if(argc!=3) { fprintf(stderr,"Usage:%s/n",argv[0]); exit(EXIT_FAILURE); } if(sem_init(&sem,0,0)==-1) handle_error("sem_init"); /*EstablishSIGALRMhandler;setalarmtimerusingargv[1]*/ sa.sa_handler=handler; sigemptyset(&sa.sa_mask); sa.sa_flags=0; if(sigaction(SIGALRM,&sa,NULL)==-1) handle_error("sigaction"); alarm(atoi(argv[1])); /*Calculaterelativeintervalascurrenttimeplus numberofsecondsgivenargv[2]*/ if(clock_gettime(CLOCK_REALTIME,&ts)==-1) handle_error("clock_gettime"); ts.tv_sec+=atoi(argv[2]); printf("main()abouttocallsem_timedwait()/n"); while((s=sem_timedwait(&sem,&ts))==-1&&errno==EINTR) continue;/*Restartifinterruptedbyhandler*/ /*Checkwhathappened*/ if(s==-1) { if(errno==ETIMEDOUT) printf("sem_timedwait()timedout/n"); else perror("sem_timedwait"); } else { printf("sem_timedwait()succeeded/n"); } exit((s==0)?EXIT_SUCCESS:EXIT_FAILURE); }
【實(shí)例 2】:
#include #include #include #include #include #include #include #include sem_tsem; void*func1(void*arg){ sem_wait(&sem); int*running=(int*)arg; printf("threadfunc1running:%d/n",*running); pthread_exit(NULL); } void*func2(void*arg) { printf("threadfunc2running./n"); sem_post(&sem); pthread_exit(NULL); } intmain(void) { inta=3; sem_init(&sem,0,0); pthread_tthread_id[2]; pthread_create(&thread_id[0],NULL,func1,(void*)&a); printf("mainthreadrunning./n"); sleep(10); pthread_create(&thread_id[1],NULL,func2,(void*)&a); printf("mainthreadstillrunning./n"); pthread_join(thread_id[0],NULL); pthread_join(thread_id[1],NULL); sem_destroy(&sem); return0; }
有名信號(hào)量
有時(shí)候也叫命名信號(hào)量,之所以稱(chēng)為命名信號(hào)量,是因?yàn)樗幸粋€(gè)名字、一個(gè)用戶(hù) ID、一個(gè)組 ID 和權(quán)限。這些是提供給不共享內(nèi)存的那些進(jìn)程使用命名信號(hào)量的接口。命名信號(hào)量的名字是一個(gè)遵守路徑名構(gòu)造規(guī)則的字符串。
接口函數(shù)
sem_open 函數(shù)
該函數(shù)用于創(chuàng)建或打開(kāi)一個(gè)命名信號(hào)量,其原型如下:
sem_t*sem_open(constchar*name,intoflag); sem_t*sem_open(constchar*name,intoflag,mode_tmode,unsignedintvalue);
參數(shù)
name 是一個(gè)標(biāo)識(shí)信號(hào)量的字符串。
oflag 用來(lái)確定是創(chuàng)建信號(hào)量還是連接已有的信號(hào)量。oflag 的參數(shù)可以為 0,O_CREAT 或 O_EXCL:如果為 0,表示打開(kāi)一個(gè)已存在的信號(hào)量;如果為 O_CREAT,表示如果信號(hào)量不存在就創(chuàng)建一個(gè)信號(hào)量,如果存在則打開(kāi)被返回,此時(shí) mode 和 value 都需要指定;如果為 O_CREAT|O_EXCL,表示如果信號(hào)量存在則返回錯(cuò)誤。
mode 用于創(chuàng)建信號(hào)量時(shí)指定信號(hào)量的權(quán)限位,和 open 函數(shù)一樣,包括:S_IRUSR、S_IWUSR、S_IRGRP、S_IWGRP、S_IROTH、S_IWOTH。
value 表示創(chuàng)建信號(hào)量時(shí),信號(hào)量的初始值。
sem_close 函數(shù)
該函數(shù)用于關(guān)閉命名信號(hào)量:
intsem_close(sem_t*);
功能:?jiǎn)蝹€(gè)程序可以用 sem_close 函數(shù)關(guān)閉命名信號(hào)量,但是這樣做并不能將信號(hào)量從系統(tǒng)中刪除,因?yàn)槊盘?hào)量在單個(gè)程序執(zhí)行之外是具有持久性的。當(dāng)進(jìn)程調(diào)用 _exit、exit、exec 或從 main 返回時(shí),進(jìn)程打開(kāi)的命名信號(hào)量同樣會(huì)被關(guān)閉。
sem_unlink 函數(shù)功能:sem_unlink 函數(shù)用于在所有進(jìn)程關(guān)閉了命名信號(hào)量之后,將信號(hào)量從系統(tǒng)中刪除:
intsem_unlink(constchar*name);
信號(hào)量操作函數(shù)與無(wú)名信號(hào)量一樣。
使用實(shí)例
#include #include #include #include #include #include #include #include #include #defineSEM_NAME"/sem_name" sem_t*p_sem; void*testThread(void*ptr){ sem_wait(p_sem); sleep(2); pthread_exit(NULL);} intmain(void){ inti=0; pthread_tpid; intsem_val=0; p_sem=sem_open(SEM_NAME,O_CREAT,0555,5); if(p_sem==NULL) { printf("sem_open%sfailed!/n",SEM_NAME); sem_unlink(SEM_NAME); return-1; } for(i=0;i7;?i++) ????{ ????????pthread_create(&pid,?NULL,?testThread,?NULL); ????????sleep(1); ????????//?pthread_join(pid,?NULL);??//?not?needed,?or?loop ????????sem_getvalue(p_sem,?&sem_val); ????????printf("semaphore?value?:?%d/n",?sem_val); ????} ???? ????sem_close(p_sem); ????sem_unlink(SEM_NAME); ???? ????return?0; }
命名和無(wú)名信號(hào)量的持續(xù)性
命名信號(hào)量是隨內(nèi)核持續(xù)的。當(dāng)命名信號(hào)量創(chuàng)建后,即使當(dāng)前沒(méi)有進(jìn)程打開(kāi)某個(gè)信號(hào)量,它的值依然保持,直到內(nèi)核重新自舉或調(diào)用 sem_unlink()刪除該信號(hào)量。
無(wú)名信號(hào)量的持續(xù)性要根據(jù)信號(hào)量在內(nèi)存中的位置確定:
如果無(wú)名信號(hào)量是在單個(gè)進(jìn)程內(nèi)部的數(shù)據(jù)空間中,即信號(hào)量只能在進(jìn)程內(nèi)部的各個(gè)線(xiàn)程間共享,那么信號(hào)量是隨進(jìn)程的持續(xù)性,當(dāng)進(jìn)程終止時(shí)他也就消失了;
如果無(wú)名信號(hào)量位于不同進(jìn)程的共享內(nèi)存區(qū),因此只要該共享內(nèi)存區(qū)仍然存在,該信號(hào)量就會(huì)一直存在;所以此時(shí)無(wú)名信號(hào)量是隨內(nèi)核的持續(xù)性。
信號(hào)量 - 互斥量 - 條件變量
很多時(shí)候信號(hào)量、互斥量和條件變量都可以在某種應(yīng)用中使用,那這三者的差異有哪些呢?下面列出了這三者之間的差異:
互斥量必須由給它上鎖的線(xiàn)程解鎖;而信號(hào)量不需要由等待它的線(xiàn)程進(jìn)行掛出,可以在其他進(jìn)程進(jìn)行掛出操作;
互斥量要么被鎖住,要么被解開(kāi),只有這兩種狀態(tài);而信號(hào)量的值可以支持多個(gè)進(jìn)程 / 線(xiàn)程成功的進(jìn)行 wait 操作;
信號(hào)量的掛出操作總是被記住,因?yàn)樾盘?hào)量有一個(gè)計(jì)數(shù)值,掛出操作總會(huì)將該計(jì)數(shù)值加 1,然而當(dāng)條件變量發(fā)送一個(gè)信號(hào)時(shí),如果沒(méi)有線(xiàn)程等待在條件變量,那么該信號(hào)就會(huì)丟失。
審核編輯 黃昊宇
-
Linux
+關(guān)注
關(guān)注
87文章
11304瀏覽量
209521 -
Posix
+關(guān)注
關(guān)注
0文章
36瀏覽量
9497 -
信號(hào)量
+關(guān)注
關(guān)注
0文章
53瀏覽量
8344
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論