




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、LINUX下進(jìn)程間信號量的使用SUNNY.MAN1.使用信號量的目的打個(gè)比方,如果兩個(gè)進(jìn)程同時(shí)操作一塊共享內(nèi)存。其中一個(gè)進(jìn)行寫數(shù)據(jù)操作,另一個(gè)讀數(shù)據(jù)操作。如果沒有使用同步信號,將會產(chǎn)生資源沖突。舉個(gè)例子解釋:當(dāng)一個(gè)進(jìn)程在0x0000這個(gè)地址寫入“this is a test!”,因?yàn)檫@不是一個(gè)原子操作,那么可能剛好寫完”this is a”時(shí),讀的進(jìn)程便開始讀數(shù)據(jù),那么它讀到的數(shù)據(jù)就會是 this is a 而不是this is a test!。這就是資源沒有同步而引起的。為了數(shù)據(jù)的完整性和資源的同步性,我們使用信號量。一句話來說,為了讓N個(gè)進(jìn)程對一個(gè)共同操作的動作順序化,我們必須使用同步機(jī)制
2、。2.信號量相關(guān)函數(shù)介紹 System V 信號量的創(chuàng)建和打開 #include int semget(key_t key, int nsems, int semflg); semget 用于創(chuàng)建或打開一個(gè)已存在的信號量。 若成功就返回非負(fù)的標(biāo)識符,否則返回-1。key : 用于生成唯一信號量的 key ,主要的目的是使不同進(jìn)程在同一該 IPC 匯合。 key 可以是事先不同的進(jìn)程約定好的一個(gè)值,也可以不同進(jìn)程通過相同的路徑名和項(xiàng)目 ID,調(diào)用 ftok() 函數(shù),生成一個(gè)鍵。 nsems :表示信號量集中信號量的個(gè)數(shù),如果創(chuàng)建一個(gè)信號量集, nsems 必須是一個(gè)非 0 正整數(shù),如果打開一個(gè)
3、指定的信號量集, nsems 可以指定為 0 。 semflag : IPC_CREAT, IPC_EXCL, 以及 IPC 的指定權(quán)限位。如果為 IPC_CREAT IPC_EXCL ,當(dāng)該信號量集以及存在會返回錯誤。 errno 為 EEXIST 。 semget 的返回值是被稱為信號量標(biāo)識符的整數(shù), semop 和 semctl 函數(shù)將通過該標(biāo)識符對信號量集進(jìn)行操作。 注:這里需要知道是調(diào) semget() 創(chuàng)建一個(gè)新的信號量集并沒有對之初始化,需要調(diào)用后面要講的 semctl() 函數(shù)進(jìn)行初始化。這樣 System V 信號量的創(chuàng)建和初始化就不是一個(gè)原子操作,這是一個(gè)很大的缺陷。會出現(xiàn)
4、使用未初始化信號量集的問題,我將在下面的段落講解如何避免這種情況。3 .System V 信號量的控制操作 #include int semctl(int semid, int semnum, int cmd, ./* union semun arg*/);/若失敗返回-1,并設(shè)置errno。semctl 函數(shù)主要是對信號量集的一系列控制操作,根據(jù)操作命令 cmd 的不同,執(zhí)行不同的操作,依賴于所請求的命令,第四個(gè)參數(shù)是可選的, semid : System V 信號量的標(biāo)識符; semnum :表示信號量集中的第 semnum 個(gè)信號量。它的取值范圍: 0nsems-1 。 cmd :操作命
5、令; arg :如果使用該參數(shù),該參數(shù)的類型為 union semun,它是多個(gè)特定命令的聯(lián)合。 按照SUS 明確規(guī)定, 這個(gè)結(jié)構(gòu)必須有用戶自己定義,在Linux 2.6.18的系統(tǒng)頭文件中也沒有該結(jié)構(gòu)的定義,但在 中有一段對該結(jié)構(gòu)的定義的建議,不過是注釋掉的。 include union semun int val; / Value for SETVAL struct semid_ds *buf; / Buffer for IPC_STAT, IPC_SET unsigned short *array; / Array for GETALL, SETALL struct seminfo *_
6、buf; / Buffer for IPC_INFO(Linux-specific);semctl 中 cmd 命令有 10 種,如下: IPC_STAT :獲取此信號量集合的 semid_ds 結(jié)構(gòu),存放在第四個(gè)參數(shù) arg 的 buf 中; IPC_SET :通過 arg.buf 來設(shè)定信號量集相關(guān)聯(lián)的 semid_ds 中信號量集合權(quán)限為 sem_perm 中的 uid , gid , mode 。 IPC_RMID :從系統(tǒng)中刪除該信號量集合。這種刪除立即發(fā)生,仍在使用該信號量集的其他進(jìn)程,在下次對該信號量集進(jìn)行操作的時(shí)候,會發(fā)生錯誤并返回 EIDRM 。這和 POSIX 信號量是不一
7、樣的。 POSIX 信號量 sem_unlink 只是會立即刪除信號量的在文件系統(tǒng)中的文件,而信號量的析構(gòu)是在最后一個(gè) sem_close 發(fā)生是進(jìn)行的。 GETVAL :返回第 semnum 個(gè)信號量的值; SETVAL :設(shè)置第 semnum 個(gè)信號量的值,該值由第四個(gè)參數(shù) arg 中的 val 指定; GETPID :返回第 semnum 個(gè)信號量的 sempid ,最后一個(gè)操作的 pid ; GETNCNT :返回第 semnum 個(gè)信號量的 semncnt 。等待 semval 變?yōu)榇笥诋?dāng)前值的線程數(shù); GETZCNT :返回第 semnum 個(gè)信號量的 semzcnt 。等待 se
8、mval 變?yōu)?0 的線程數(shù)。 GETALL :去信號量集合中所有信號量的值,將結(jié)果存放到 arg 中的 array 所指向的數(shù)組。 SETALL :按 arg.array 所指向的數(shù)組中的值,設(shè)置集合中所有信號量的值。 對于 GETALL 以外的所有 GET 命令, semctl 都返回相應(yīng)的值,其他命令的返回值為 0 ;4. System V 信號量的信號量操作 #include int semop(int semid, struct sembuf *sops, unsigned nsops);int semtimedop(int semid, struct sembuf *sops, u
9、nsigned nsops, struct timespec *timeout);/成功返回0,出錯返回-1semop 函數(shù)主要是在已打開的信號量集上,對其中的 一個(gè)或多個(gè)信號量的值進(jìn)行操作 。 semid : System V 信號量的標(biāo)識符,用來標(biāo)識一個(gè)信號量集。 sops :是指向一個(gè)struct sembuf 結(jié)構(gòu)體數(shù)組的指針,該數(shù)組是一個(gè)信號量操作數(shù)組。 nsops : sops 所指向 sembuf 結(jié)構(gòu)體數(shù)組中元素的個(gè)數(shù)。 sembuf 結(jié)構(gòu)體的定義如下: struct sembuf unsigned short int sem_num; /* 信號量的序號從0nsems-1 *
10、/ short int sem_op; /* 對信號量的操作,0, 0, 0 ,對該信號量執(zhí)行掛出操作,掛出的值由 sem_op 決定,系統(tǒng)會把 sem_op 的值加到該信號量的當(dāng)前值 semval (參考文章開頭關(guān)于每個(gè)信號量結(jié)構(gòu)的定義)上。如果 sem_flag 指定了 SEM_UNDO (還原)標(biāo)志,那么相應(yīng)信號量的 semadj 值會減掉 sem_op 的值。下面會說明 semadj 的含義。 sem_op = -sem_op 時(shí), semval 減掉 sem_op 的絕對值,為該線程分配對應(yīng)數(shù)目的資源。如果指定 SEM_UNDO ,相應(yīng)信號量的 semadj 就加上 sem_op 的
11、絕對值。當(dāng) semval = -sem_op ,當(dāng)此條件滿足時(shí),調(diào)用線程被喚醒,執(zhí)行相應(yīng)的分配操作,然后 semncnt 減去 1. sem_op = 0 ,表示調(diào)用者希望 semval 變?yōu)?0 。如果為 0 則立即返回,如果不為 0 ,相應(yīng)信號量的 semzcnt 加 1 ,調(diào)用調(diào)用線程被阻塞。 sem_flag :信號量操作的屬性標(biāo)志,如果為 0,表示正常操作,如果為 IPC_WAIT ,使對信號量的操作時(shí)非阻塞的。即指定了該標(biāo)志,調(diào)用線程在信號量的值不滿足條件的情況下不會被阻塞,而是直接返回 -1 ,并將 errno 設(shè)置為 EAGAIN 。如果為 SEM_UNDO ,那么將維護(hù)進(jìn)程對
12、信號量的調(diào)整值,以便進(jìn)程結(jié)束時(shí)恢復(fù)信號量的狀態(tài)。注:semval :信號量的當(dāng)前值,在文章開頭信號量的結(jié)構(gòu)中已提到。 semncnt :等待 semval 變?yōu)榇笥诋?dāng)前值的線程數(shù)。在文章開頭信號量的結(jié)構(gòu)中已提到。 semzcnt :等待 semval 變?yōu)?0 的線程數(shù)。在文章開頭信號量的結(jié)構(gòu)中已提到。 semadj :指定信號量針對某個(gè)特定進(jìn)程的調(diào)整值。只有 sembuf 結(jié)構(gòu)的 sem_flag 指定為 SEM_UNDO 后, semadj 才會隨著 sem_op 而更新。講簡單一點(diǎn):對某個(gè)進(jìn)程,在指定 SEM_UNDO 后,對信號量 semval 值的修改都會反應(yīng)到 semadj 上,當(dāng)
13、該進(jìn)程終止的時(shí)候,內(nèi)核會根據(jù) semadj 的值,重新恢復(fù)信號量之前的值 。5 .System V 信號量的繼承和銷毀 父進(jìn)程中打開的信號量在子進(jìn)程中仍然是保持著打開狀態(tài)的。 對于一個(gè)持有該信號量的進(jìn)程在沒有釋放該信號量的情況下就終止了。那么該進(jìn)程所占用的信號量內(nèi)核是不會進(jìn)行釋放的。當(dāng)通過 semctl 傳入 IPC_RMID 對該信號量集進(jìn)行刪除時(shí),會立即將該信號量從系統(tǒng)中徹底刪除,不能再對該信號量進(jìn)行任何訪問。第三個(gè)參數(shù)cmd為IPC_RMID時(shí),整個(gè)信號集被刪除,第二個(gè)參數(shù)semnum被忽略。注:這就產(chǎn)生了一個(gè)最后退出的進(jìn)程才可以銷毀信號量,否則退出的進(jìn)程銷毀信號量后會產(chǎn)生資源的爭奪問題
14、。在下面的例子中,我會講如何避免這種情況的發(fā)生。6.信號量使用的一個(gè)具體例子6.1打開信號量并進(jìn)行初始化:兩種組合IPC_CREAT|IPC_EXCL|00666和IPC_CREAT|00666的不同點(diǎn),第一個(gè)如果沒有這個(gè)信號則創(chuàng)建一個(gè),如果已經(jīng)存在則產(chǎn)生一個(gè)存在錯誤。第二個(gè)參數(shù)如果沒有創(chuàng)建信號,有則返回已經(jīng)存在的信號ID。/主要是防止兩個(gè)進(jìn)程之間從創(chuàng)建信號量到初始化信號量的競爭static int createsem(key_t key,int initval)int flag1=IPC_CREAT|IPC_EXCL|00666;int flag2=IPC_CREAT|00666;int i
15、=0;int semid=semget(key,1,flag1);if(semid0)/如果不存在則自己創(chuàng)建,存在則看是否已經(jīng)被初始化int tmperrno=errno;if(tmperrno=EEXIST)/不是自己創(chuàng)建的semid=semget(key,1,flag2);if(semid0)return -1;int init_ok=0;for(i=0; isem_otime!=0)i=3;init_ok=1;elseusleep(300000);/等300ms/forif(!init_ok)/如果待900ms仍然沒有初始化,可證明創(chuàng)建的進(jìn)程沒有進(jìn)行初始化union semun arg;
16、arg.val=initval;if(semctl(semid,0,SETVAL,arg)=-1) perror(semctl setval error);elsesemaphore_v(semid);/使用這個(gè)用改變sem_otime,以便告訴其它進(jìn)程已經(jīng)被初始化可以使用了。先V后P以保證初值為0時(shí)的操作性semaphore_p(semid);return semid;/if(!init_ok)elsereturn semid;/if existelseperror(semget error); / esle tmperrno=EEXISTelse/自己是創(chuàng)建者,則進(jìn)行初始化union se
17、mun arg;arg.val=initval;if(semctl(semid,0,SETVAL,arg)=-1)perror(semctl setval error);elsesemaphore_v(semid);semaphore_p(semid);return semid;return -1;6.2信號量的V和P操作static int semaphore_p(int sem_id) struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &
18、sem_b, 1) = -1) fprintf(stderr, semaphore_p failed/n); return 0; return 1; /semaphore_v函數(shù)將sembuf結(jié)構(gòu)的sem_op部分設(shè)置為1,從而信號量變得可用。static int semaphore_v(int sem_id) struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) = -1) fprintf(stderr, semaphore
19、_v failed/n); return 0; return 1; 6.3設(shè)置初值和刪除操作static int set_semvalue(int sem_id,int val) union semun sem_union; sem_union.val = val; if(semctl(sem_id, 0, SETVAL, sem_union) = -1) return 0; return 1;static void del_semvalue(int sem_id) union semun sem_union; if(semctl(sem_id, 0, IPC_RMID, sem_union)
20、 = -1) fprintf(stderr, Failed to delete semaphore/n);6.4如何確保最后一個(gè)進(jìn)程刪除信號量1. int sem_iddel=createsem(key_t)1235,0);/并初始化為val=02. semaphore_v(sem_iddel);每個(gè)使用的進(jìn)程在程序開始時(shí)都V這個(gè)信號量,請注意一定要用SEM_UNDO標(biāo)志3. semaphore_p(sem_iddel);每個(gè)進(jìn)程在退出的時(shí)候都要p這個(gè)信號量4.int val=semctl(sem_iddel,0,GETVAL,arg);并對信號量的值進(jìn)行查看,如果和初值相等,則證明大家都已經(jīng)
21、不用了,可以進(jìn)行刪除操作了。if(!val)del_semvalue(sem_iddel);del_semvalue(sem_id);下面這個(gè)例子,不能解決創(chuàng)建和初值問題(創(chuàng)建完成,但沒初始化時(shí))。/解決信號量的創(chuàng)建和初始化不是原子操作的一種方案 if (semId = semget(CreateKey(SEM_PATHNAME), 1, IPC_CREAT | IPC_EXCL | 0666) = 0) arg.val = 4; if (semctl(semId, 0, SETVAL, arg) 0) coutsemctl error strerror(errno)endl; return
22、-1; else if (errno = EEXIST) semId = semget(CreateKey(SEM_PATHNAME), 1, 0666); else coutsemget error strerror(errno)endl; return -1; 7.一個(gè)寫,多讀的例子使有兩信號量解決同一時(shí)間只有一個(gè)進(jìn)程可以寫,但同一時(shí)間可以有很多進(jìn)程可讀的例子,在這里只提供流程圖,就不寫代碼了。參照上面的代碼很容易就可以寫出下列流程圖的代碼。8.具體代碼的封裝操作和一個(gè)實(shí)現(xiàn)的例子8.1. sunnysem.h的代碼/FileName sunnysem.h/author:sunny.man/
23、date:2015-07-28#ifndef _SUNNYSEM_H_#define _SUNNYSEM_H_/-#include #include #include #include #include #include #include /-#define max_tries 3union semun int val; struct semid_ds *buf; unsigned short *array; ; /-extern int createsem(key_t key,int initval);extern int semaphore_p(int sem_id);extern int
24、 semaphore_v(int sem_id);extern int del_semv(int sem_id);extern int semaphore_waitz(int sem_id);/-#endif8.2.sunnysem.c的代碼/filename:sunnysem.c/-#include sunnysem.h/-static int semaphore_pp(int sem_id,unsigned short index);/內(nèi)部使用static int semaphore_vp(int sem_id,unsigned short index);static int isinit
25、ialization(int sem_id);/看是否已經(jīng)被初始化static int setsecondsemotime(int semid,int initval);/-static int isinitialization(int sem_id)/看是否已經(jīng)被初始化int i=0;for(i=0; isem_otime!=0)return 1;elseusleep(300000);/等300ms/forreturn 0;/-static int setsecondsemotime(int semid,int initval)/設(shè)置第二信號的最后時(shí)間,以用來開始union semun ar
26、g;short array2=initval,0;arg.array=array;if(semctl(semid,0,SETALL,arg)=-1) perror(semctl setval error);return 0;else/fprintf(stderr, setsecondsemotime shgetsuccess!n);semaphore_vp(semid,1);/先v以用來改變o_time的值,同時(shí)表明自己正在使用信號return 1;/-/主要是防止兩個(gè)進(jìn)程之間從創(chuàng)建信號量到初始化信號量的競爭int createsem(key_t key,int initval)int fla
27、g1=IPC_CREAT|IPC_EXCL|00666;int flag2=IPC_CREAT|00666;int i=0;int semid=semget(key,2,flag1);/第一個(gè)信號用來使用,第二個(gè)信號用來最后一個(gè)進(jìn)程進(jìn)行刪除信號操作if(semid=0)int init_ok=isinitialization(semid);if(!init_ok)/如果待900ms仍然沒有初始化,可證明第一個(gè)進(jìn)程沒有進(jìn)行初始化if(setsecondsemotime(semid,initval)return semid;/if(!init_ok)elsesemaphore_vp(semid,1
28、);/表明自己使用信號量return semid;/semid =0/if existelseperror(semget error); / esle tmperrno=EEXISTelse/自己是創(chuàng)建者,則進(jìn)行初始化/fprintf(stderr, semaphore shgetsuccess!n);if(setsecondsemotime(semid,initval)return semid;return -1;/-/給一個(gè)信號量集里的第幾個(gè)信號附值static int semaphore_pp(int sem_id,unsigned short index) struct sembuf
29、sem_b; sem_b.sem_num = index; sem_b.sem_op = -1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) = -1) fprintf(stderr, semaphore_p failedn); return 0; /fprintf(stderr, semaphore_pp success!n); return 1; /-/semaphore_v函數(shù)將sembuf結(jié)構(gòu)的sem_op部分設(shè)置為1,從而信號量變得可用。static int semaphore_vp(int sem_id,unsigne
30、d short index) struct sembuf sem_b; sem_b.sem_num = index; sem_b.sem_op = 1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) = -1) fprintf(stderr, semaphore_v failedn); return 0; /fprintf(stderr, semaphore_vp successn); return 1; /-/等待信號變成0 int semaphore_waitz(int sem_id) struct sembuf sem_b;
31、sem_b.sem_num = 0; sem_b.sem_op = 0; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) = -1) fprintf(stderr, semaphore_v failedn); return 0; return 1; /-/給一個(gè)信號量集里的第幾個(gè)信號附值int semaphore_p(int sem_id)return semaphore_pp(sem_id,0);/-/semaphore_v函數(shù)將sembuf結(jié)構(gòu)的sem_op部分設(shè)置為1,從而信號量變得可用。int semaphore_v(int
32、sem_id)return semaphore_vp(sem_id,0);/-int del_sem(int sem_id) union semun arg; union semun sem_union; semaphore_pp(sem_id,1);/和信號量斷開連接 if(semctl(sem_id,1,GETVAL,arg)=0) if(semctl(sem_id, 0, IPC_RMID, sem_union) = -1) fprintf(stderr, Failed to delete semaphoren);return 1; return 0;/-8.3.測試的代碼/filename:main.c/function:測試信號量的使用方法#include #include #include #i
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 鐵路護(hù)路聯(lián)防培訓(xùn)課件
- 口腔口咽部解剖
- 班長質(zhì)量培訓(xùn)課件模板
- 小學(xué)科學(xué)呼吸道傳染病認(rèn)知
- 年產(chǎn)xx萬件貨架項(xiàng)目可研報(bào)告
- 2025-2030中國斯諾克用品市場應(yīng)用潛力與未來供需平衡預(yù)測報(bào)告
- 2025版綠色能源項(xiàng)目碳排放權(quán)交易合同模板
- 二零二五年度房地產(chǎn)異業(yè)聯(lián)盟社區(qū)物流合作合同范本
- 2025年文化旅游廣告場地租賃合同書
- 2025版梁蘭離婚協(xié)議中涉及股權(quán)、房產(chǎn)等財(cái)產(chǎn)分配與離婚協(xié)議
- 基孔肯雅熱防控技術(shù)指南(2025年版)宣講課件
- 眼疾病課件教學(xué)課件
- 2025年機(jī)械制造行業(yè)技能考試-制動鉗工(客車)歷年參考題庫含答案解析(5套100道單選題合輯)
- 骨科快速康復(fù)護(hù)理課件
- 2025年基本公共衛(wèi)生服務(wù)中醫(yī)藥健康管理服務(wù)項(xiàng)目培訓(xùn)考試試題(含答案)
- 2025小紅書閉環(huán)電商推廣投放產(chǎn)品與方法論
- (高清版)DB11∕T 509-2025 房屋建筑修繕工程定案和施工質(zhì)量驗(yàn)收規(guī)程
- 暑假社區(qū)托管活動方案
- 智算中心及算力產(chǎn)業(yè)集群項(xiàng)目節(jié)能評估報(bào)告
- 中華人民共和國學(xué)前教育法測試題含參考答案(共3套)
- 礦山機(jī)電安全培訓(xùn)
評論
0/150
提交評論