Linux訊號量PV操作
semop操作中:sembuf結構的sem_flg成員可以為0、IPC_NOWAIT、SEM_UNDO 。為SEM_UNDO時,它將使作業系統跟蹤當前進程對這個訊號量的修改情況,如果這個進程在沒有釋放該訊號量的情況下終止,作業系統將自動釋放該進程持有的
semop操作中:sembuf結構的sem_flg成員可以為0、IPC_NOWAIT、SEM_UNDO 。為SEM_UNDO時,它將使作業系統跟蹤當前進程對這個訊號量的修改情況,如果這個進程在沒有釋放該訊號量的情況下終止,作業系統將自動釋放該進程持有的訊號量。除非你對訊號量的行為有特殊的要求,否則應該養成設定sem_flg為SEM_UNDO的好習慣。
1: //假設兩個進程(父子進程)對一個檔案進行寫操作,但是這個檔案同一時間只能有一個進程進行寫操作。
2: //利用訊號量實現pv操作
3: #include <stdio.h>
4: #include <stdlib.h>
5: #include <sys/ipc.h>
6: #include <sys/sem.h>
7: #include <sys/types.h>
8: #include <fcntl.h>
9: struct sembuf sops;
10: static int sid;
11: //建立一個新的訊號量集
12: int createSemset(void)
13: {
14: char* pathname="semset";
15: if( access(pathname, F_OK)!=0 )
16: {
17: int fd=open(pathname, O_RDWR | O_CREAT, 0666);
18: if( fd<0 )
19: {
20: perror("open");
21: return -1;
22: }
23: }
24: key_t key=ftok(pathname, 'a');
25: if( -1==key )
26: {
27: perror("ftok");
28: return -1;
29: }
30: return semget(key, 1, IPC_CREAT | 0666) ;
31: }
32:
33: //P操作
34: int P(void)
35: {
36: sops.sem_num=0;
37: sops.sem_op=-1;
38: sops.sem_flg=0;
39: return semop(sid, &sops, 1);
40: }
41: //V操作
42: int V(void)
43: {
44: sops.sem_num=0;
45: sops.sem_op=1;
46: sops.sem_flg=0;
47: return semop(sid, &sops, 1);
48: }
49: int main(int argc, char *argv[])
50: {
51: sid=createSemset();
52: if( -1==sid )
53: {
54: perror("createSemset");
55: exit(1);
56: }
57:
58: if( -1==semctl(sid, 0, SETVAL, 1) )
59: {
60: perror("SETVAL");
61: exit(1);
62: }
63: pid_t pid=fork();
64: if( pid<0 )
65: {
66: perror("fork");
67: exit(1);
68: }
69: else if( 0==pid )
70: {
71: while(1)
72: {
73: if( -1==P() )
74: {
75: printf("P操作失敗!
");
76: exit(1);
77: }
78: printf("子進程正在對檔案進行寫操作!
");
79: sleep(1);
80: printf("子進程寫操作完畢,釋放資源!
");
81: if( -1==V() )
82: {
83: printf("V操作失敗!");
84: exit(1);
85: }
86: }
87: }
88: else
89: {
90: while(1)
91: {
92: if( -1==P() )
93: {
94: printf("P操作失敗!
");
95: exit(1);
96: }
97: printf("父進程進程正在對檔案進行寫操作!
");
98: sleep(1);
99: printf("父進程寫操作完畢,釋放資源!
");
100: if( -1==V() )
101: {
102: printf("V操作失敗!");
103: exit(1);
104: } ......
1: void P(int semid)
2: {
3: struct sembuf sem_p;
4: sem_p.sem_num = 0;
5: sem_p.sem_op = -1;
6: sem_p.sem_flg = SEM_UNDO;
7: if (semop(semid, &sem_p, 1) == -1) {
8: perror("p op failed
");
9: exit(1);
10: }
11: }
12:
13: void V(int semid)
14: {
15: struct sembuf sem_p;
16: sem_p.sem_num = 0;
17: sem_p.sem_op = 1;
18: sem_p.sem_flg = SEM_UNDO;
19: if (semop(semid, &sem_p, 1) == -1) {
20: perror("v op failed
");
21: exit(1);
22: }
23: }
PV原語通過操作訊號量來處理進程間的同步與互斥的問題。其核心就是一段不可分割不可中斷的程式。
訊號量的概念1965年由著名的荷蘭電腦科學家Dijkstra提出,其基本思路是用一種新的變數類型(semaphore)來記錄當前可用資源的數量。有兩種實現方式:1)semaphore的取值必須大於或等於0。0表示當前已沒有空閑資源,而正數表示當前空閑資源的數量;2)semaphore的取值可正可負,負數的絕對值表示正在等待進入臨界區的進程個數。
訊號量是由作業系統來維護的,使用者進程只能通過初始化和兩個標準原語(P、V原語)來訪問。初始化可指定一個非負整數,即空閑資源總數。
P原語:P是荷蘭語Proberen(測試)的首字母。為阻塞原語,負責把當前進程由運行狀態轉換為阻塞狀態,直到另外一個進程喚醒它。操作為:申請一個空閑資源(把訊號量減1),若成功,則退出;若失敗,則該進程被阻塞;
V原語:V是荷蘭語Verhogen(增加)的首字母。為喚醒原語,負責把一個被阻塞的進程喚醒,它有一個參數表,存放著等待被喚醒的進程資訊。操作為:釋放一個被佔用的資源(把訊號量加1),如果發現有被阻塞的進程,則選擇一個喚醒之。
具體PV原語對訊號量的操作可以分為三種情況:
1)把訊號量視為一個加鎖標誌位,實現對一個共用變數的互斥訪問。
實現過程:
P(mutex); // mutex的初始值為1 訪問該共用資料;
V(mutex);
非臨界區
2)把訊號量視為是某種類型的共用資源的剩餘個數,實現對一類共用資源的訪問。
實現過程:
P(resource); // resource的初始值為該資源的個數N 使用該資源;
V(resource); 非臨界區
3)把訊號量作為進程間的同步工具
實現過程:
臨界區C1;
P(S);
V(S);
臨界區C2;