Linux進程互斥 小實驗(1) 模擬生產者-消費者

實驗內容

服務進程作爲消費者, 只有一個, 打印當前共享存儲區的數值
客戶進程作爲生產者, 用來接收用戶輸入數值, 然後保存到共享存儲區

服務進程會一直循環執行, 直到用戶終止它
客戶進程也會一直循環執行, 等待用戶的輸入, 用戶如果輸入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);
}

運行結果

在這裏插入圖片描述
步驟

  1. 運行程序, 作爲服務進程
  2. ctrl+z 將進程 suspend 到後臺
  3. bg 讓服務進程在後臺運行
  4. 再運行程序, 作爲客戶進程

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章