實驗內容
服務進程作爲消費者, 只有一個, 打印當前共享存儲區的數值
客戶進程作爲生產者, 用來接收用戶輸入數值, 然後保存到共享存儲區
服務進程會一直循環執行, 直到用戶終止它
客戶進程也會一直循環執行, 等待用戶的輸入, 用戶如果輸入0則退出
實驗分析
- 生產者消費者問題需要兩個資源信號量
- 可以設置資源信號量S1, 說明當前共享存儲區是否爲空, 初值爲1; 資源信號量S2, 說明當前共享存儲區是否已修改, 初值爲0
所以會先執行生產者, 然後再消費者
會用到的函數
可以用到再到這裏查, 有漏的可以看參考資料
- shmget() 建立一個共享存儲區
int shmget(key_t key, size_t size, int flag);
- 返回值: 創建成功返回共享存儲的id, 失敗返回 -1
- shmctl() 操縱一個共享存儲區
- shmat() 把一個共享存儲區附接到進程內存空間
- shmdt() 把一個已經附接的共享存儲區從進程內存空間斷開
- semget() 建立一個信號量或取得一個已有信號量
int semget(key_t key, int num_sems, int sem_flags);
- 返回值: 成功返回一個相應信號標識符(非零), 失敗返回 -1
- key:
- 整數值, 唯一非零
- 通過它可以訪問一個信號量: 程序對信號量的訪問是間接的. 程序得通過調用 semget() 並提供一個鍵, 再由系統生成一個相應的信號標識符, 即 semget() 的返回值; , 只有 semget() 才直接使用信號量鍵.
- num_sems: 需要的信號量數目, 一般是1
- sem_flags:
- IPC_CREAT
- IPC_EXCL
- IPC_CREAT | IPC_EXCL
- semctl() 操縱一個信號量集,包括賦初值
- semop() 對信號量集進行wait和signal操作
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
- sem_id是由semget返回的信號量標識符,sembuf結構的定義如下:
struct sembuf{ short sem_num; //除非使用一組信號量,否則它爲0 short sem_op; //信號量在一次操作中需要改變的數據,通常是兩個數,一個是-1,即P(等待)操作, //一個是+1,即V(發送信號)操作。 short sem_flg; //通常爲SEM_UNDO,使操作系統跟蹤信號, //並在進程沒有釋放該信號量而終止時,操作系統釋放信號量 };
- signal() 設置對信號的處理方式或處理過程
實驗代碼
semaphere.c
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define MY_SHMKEY 10071500 // 如果已經被使用了就需要修改
#define MY_SEMKEY 10071500 // 如果已經被使用了就需要修改
void sigend(int);
int shmid; // 共享存儲區id
int semid; // 信號量id
int main(void)
{
int *shmptr, semval, local;
struct sembuf semopbuf;
if ((shmid = shmget(MY_SHMKEY, sizeof(int), IPC_CREAT | IPC_EXCL | 0666)) < 0) // 第一次運行創建存儲區, 並返回-1
{ /* shared memory exists, act as client */
shmid = shmget(MY_SHMKEY, sizeof(int), 0666); // 建立一個共享存儲區
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; // P操作
semopbuf.sem_flg = SEM_UNDO; // 使操作系統跟蹤信號,並在進程沒有釋放該信號量而終止時,操作系統釋放信號量
semop(semid, &semopbuf, 1); /* P(S1) */ // 改變信號量的值
*shmptr = local; // 共享存儲區的值設置爲用戶輸出值
semopbuf.sem_num = 1; // 使用第二組信號量
semopbuf.sem_op = 1; // V操作, 發送信號
semopbuf.sem_flg = SEM_UNDO;
semop(semid, &semopbuf, 1); /* V(S2) */
printf("Input a number:\n");
scanf("%d", &local);
}
}
else /* acts as server */
{
shmptr = (int *)shmat(shmid, 0, 0); // 獲得共享存儲區地址
// 信號量的初始化
semid = semget(MY_SEMKEY, 2, IPC_CREAT | 0666); // 建立兩個信號量
semval = 1; // S1 信號量初始值
semctl(semid, 0, SETVAL, semval); /* set S1=1 */ // 給第一個信號量S1賦初值爲1
semval = 0; // S2 信號量初始值
semctl(semid, 1, SETVAL, semval); /* set S2=0 */ // 給第二個信號量S2賦初值爲0
signal(SIGINT, sigend);
signal(SIGTERM, sigend); // 退出, 如ctrl+C時執行sigend
printf("ACT CONSUMER!!! To end, try Ctrl+C or use kill.\n\n");
while (1)
{
semopbuf.sem_num = 1; // 使用第二組信號量
semopbuf.sem_op = -1; // P操作
semopbuf.sem_flg = SEM_UNDO;
semop(semid, &semopbuf, 1); /* P(S2) */
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);
}
運行結果
步驟
- 運行程序, 作爲服務進程
ctrl+z
將進程 suspend 到後臺- bg 讓服務進程在後臺運行
- 再運行程序, 作爲客戶進程
- …
reference
- https://blog.csdn.net/zhouzhenhe2008/article/details/72891957
- https://blog.csdn.net/qustdjx/article/details/7708311
- https://blog.csdn.net/ljianhui/article/details/10243617