在使用共享存儲區通信時會遇到當多人同時運行客戶端代碼通過共享存儲區與服務器代碼做應答時,共享存儲區內容還沒有來得及顯示,就被惡意篡改的情況(上篇文章中有源碼 http://www.linuxidc.com/Linux/2012-01/52541.htm)。本文將通過Linux下的信號量機制(pv操作)解決這個問題。
PV操作的基本原理是在計算機操作系統課中學到的,這裡不再詳述。然而在Linux下PV操作基本編程是在下面的代碼中學到的,現貼出來(注釋是自己百度後添加的,裡面很多參數的設置需要費一番功夫弄明白。):
[cpp]
- //注意:在Linux下編程,中文注釋有可能編譯通不過。提示錯誤為:編碼不對。(以下代碼在加注釋後便出現此問題,編譯時去掉注釋)
- #include <sys/types.h>
- #include <unistd.h>
- #include <signal.h>//signal的頭文件。
- #include <stdio.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/sem.h>
- #define MY_SHMKEY 10071500 // need to change
- #define MY_SEMKEY 10071500 // need to change
- void sigend(int);//清除操作
- int shmid, semid;
- int main(void)
- {
- int *shmptr, semval, local;
- struct sembuf semopbuf; //sembuf為信號量的數據結構。設置semopbuf中的值,通過semop指定,可改變當前指定信號量數據結構中的值。
- if((shmid=shmget(MY_SHMKEY, sizeof(int), IPC_CREAT|IPC_EXCL|0666)) < 0)//IPC_CREAT如果不存在則創建,如果存在則打開。IPC_EXCL只有不存在的時候才創建,如果系統中一存在,則錯誤。二者同時使用保證此共享內存為新建的。0666為可讀可寫權限。
- { /* shared memory exists, act as client */
- shmid=shmget(MY_SHMKEY, sizeof(int), 0666);//獲取共享存儲區的ID
- semid=semget(MY_SEMKEY, 2, 0666);//獲取信號量集ID
- shmptr=(int *)shmat(shmid, 0, 0);
- printf("Act as producer. To end, input 0 when prompted.\n\n");
- printf("Input a number:\n");
- scanf("%d", &local);
- while( local )
- {
- semopbuf.sem_num=0;//指定操作信號量集中的第一個信號量
- semopbuf.sem_op=-1;
- semopbuf.sem_flg=SEM_UNDO;
- semop(semid, &semopbuf, 1); //操作一個信號量。(p(s1))
- *shmptr = local;
- semopbuf.sem_num=1;
- semopbuf.sem_op=1;
- semopbuf.sem_flg=SEM_UNDO;
- semop(semid, &semopbuf, 1); /* V(S2) */
- printf("Input a number:\n");
- scanf("%d", &local);
- }
- }
- else /* acts as server */
- {
- semid=semget(MY_SEMKEY, 2, IPC_CREAT|0666);//創建信號量集(信號量被創建的情況有兩種:1.鍵值為IPC_PRIVATE 2.指定一個系統中不存在的信號量集key值。同時標志中指定IPC_CREAT),2為信號量個數,即當前信號量集中有兩個信號量
- shmptr=(int *)shmat(shmid, 0, 0);//共享存儲區掛接到shmptr上
- semval=1;
- semctl(semid, 0, SETVAL, semval);//SETVAL:設置信號量中一個單獨的信號量的值。
- semval=0;
- semctl(semid, 1, SETVAL,semval); /* set S2=0 */
- signal(SIGINT, sigend);//設置某一信號的對應動作.ctrl+c引發SIGINT,Kill命令引發SIGTERM
- signal(SIGTERM, sigend);
- printf("ACT CONSUMER!!! To end, try Ctrl+C or use kill.\n\n");
- while(1)
- {
- semopbuf.sem_num=1;//指定信號量集中的第二個信號量
- semopbuf.sem_op=-1;//如果其值為正數,該值會加到現有的信號內含值中。通常用於釋放所控資源的使用權;如果sem_op的值為負數,而其絕對值又大於信號的現值,操作將會阻塞,直到信號值大於或等於sem_op的絕對值。通常用於獲取資源的使用權;如果sem_op的值為0,則操作將暫時阻塞,直到信號的值變為0。
- semopbuf.sem_flg=SEM_UNDO;//SEM_UNDO保證程序異常結束時信號量的值回復到調用semop前的值。
- semop(semid, &semopbuf, 1); /* P(S2) *///第三個參數指定本次操作的信號操作數量,必>1。
- printf("Shared memory set to %d\n", *shmptr);
- semopbuf.sem_num=0;
- semopbuf.sem_op=1;
- semopbuf.sem_flg=SEM_UNDO;
- semop(semid, &semopbuf, 1); /* V(S1) */
- }
- }
- }
-
- void sigend(int sig)
- {
- shmctl(shmid, IPC_RMID, 0);
- semctl(semid, IPC_RMID, 0);
- exit(0);
- }
下面是我的代碼,在上次代碼的基礎上給Serve和Client做PV操作。