Linux進程互斥 小實驗 模擬臨界資源訪問

實驗內容

臨界資源是一個建立在共享存儲區的

  • 服務進程 建立並初始化棧, 初始狀態下共享棧滿,裏面順序放置一系列正整數(自棧頂向下:1,2,3…), 可以與客戶進程的棧進行交換;
  • 客戶進程 有自己的本地棧, 可以對共享棧的數據進程存取;

程序中getblock()過程從共享棧中彈出一個塊號, 分配給本地棧, relblock過程把本地棧的一個塊號壓入共享棧. 爲簡單起見,已分配塊號在本地也使用棧結構保存,因而每次釋放的是最後分配的塊號

沒用PV操作的代碼

代碼註釋可以參考 第一篇

#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define MY_SHMKEY 10071800
#define MAX_BLOCK 1024
#define MAX_CMD 8

struct shmbuf
{
    int top;
    int stack[MAX_BLOCK];
} * shmptr, local;

char cmdbuf[MAX_CMD];
int shmid, semid;

void sigend(int);
void relblock(void);
int getblock(void);
void showhelp(void);
void showlist(void);
void getcmdline(void);

int main(void)
{

    if ((shmid = shmget(MY_SHMKEY, sizeof(struct shmbuf), IPC_CREAT | IPC_EXCL | 0666)) < 0)
    { /* shared memory exists, act as client */
        shmid = shmget(MY_SHMKEY, sizeof(struct shmbuf), 0666);  
        shmptr = (struct shmbuf *)shmat(shmid, 0, 0);
        local.top = -1;  // 本地棧一開始爲空
        showhelp();
        getcmdline();
        while (strcmp(cmdbuf, "end\n"))
        {
            if (!strcmp(cmdbuf, "get\n"))
                getblock();
            else if (!strcmp(cmdbuf, "rel\n"))
                relblock();
            else if (!strcmp(cmdbuf, "list\n"))
                showlist();
            else if (!strcmp(cmdbuf, "help\n"))
                showhelp();
            getcmdline();
        }
    }
    else /* acts as server */
    {
        int i;
        shmptr = (struct shmbuf *)shmat(shmid, 0, 0);
        signal(SIGINT, sigend);
        signal(SIGTERM, sigend);
        printf("NO OTHER OPERATION but press Ctrl+C or use kill to end.\n");
        shmptr->top = MAX_BLOCK - 1;  // 一開始棧滿
        for (i = 0; i < MAX_BLOCK; i++)  // 棧裏順序放置一系列正整數
            shmptr->stack[i] = MAX_BLOCK - i;
        sleep(1000000); /* cause sleep forever. */
    }
}

void sigend(int sig)
{
    shmctl(shmid, IPC_RMID, 0);
    semctl(semid, IPC_RMID, 0);
    exit(0);
}

void relblock(void)
{
    if (local.top < 0)
    {
        printf("No block to release!");
        return;
    }
    shmptr->top++;  // 移動共享棧的棧頂指針
    sleep(3);  // 用來發現代碼的缺陷
    shmptr->stack[shmptr->top] = local.stack[local.top--];  // 共享棧壓入本地棧的一個塊號
}

int getblock(void)
{
    if (shmptr->top < 0)
    {
        printf("No free block to get!");
        return;
    }
    local.stack[++local.top] = shmptr->stack[shmptr->top];  // 共享棧彈出一個塊號到本地棧
    sleep(3);  // 用來發現代碼的缺陷
    shmptr->top--;
}

void showhelp(void)
{
    printf("\navailable COMMAND:\n\n");
    printf("help\tlist this help\n");
    printf("list\tlist all gotten block number\n");
    printf("get\tget a new block\n");
    printf("rel\trelease the last gotten block\n");
    printf("end\texit this program\n");
}

void showlist(void)
{
    int i;
    printf("List all gotten block number:\n");
    for (i = 0; i <= local.top; i++)
        printf("%d\t", local.stack[i]);
}

void getcmdline(void)
{
    printf("\n?> ");
    fgets(cmdbuf, MAX_CMD - 1, stdin);
}

程序的問題

由於沒有對臨界資源(共享棧)的訪問進行限制, 可能會產生異常
例如: 開兩個客戶進程, 各有本地棧1, 2, 如果本地棧1發出get請求後, 共享棧的指針還沒來得及移動, 本地棧2也發出了一個get請求, 就會導致共享棧彈出兩個相同的數值給兩個客戶進程
如下圖
在這裏插入圖片描述

加了PV操作的代碼

修改了main(), getblock(), relblock()

#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 10071800 // need to change
#define MY_SEMKEY 10071500 // need to change
#define MAX_BLOCK 1024
#define MAX_CMD 8

struct shmbuf
{
    int top;
    int stack[MAX_BLOCK];
} * shmptr, local;

struct sembuf semopbuf;

char cmdbuf[MAX_CMD];
int shmid, semid, semval;

void sigend(int);
void relblock(void);
int getblock(void);
void showhelp(void);
void showlist(void);
void getcmdline(void);

int main(void)
{
    if ((shmid = shmget(MY_SHMKEY, sizeof(struct shmbuf), IPC_CREAT | IPC_EXCL | 0666)) < 0)
    { /* shared memory exists, act as client */
        shmid = shmget(MY_SHMKEY, sizeof(struct shmbuf), 0666);
        shmptr = (struct shmbuf *)shmat(shmid, 0, 0);
        local.top = -1;
        showhelp();
        getcmdline();
        while (strcmp(cmdbuf, "end\n"))
        {
            if (!strcmp(cmdbuf, "get\n"))
                getblock();
            else if (!strcmp(cmdbuf, "rel\n"))
                relblock();
            else if (!strcmp(cmdbuf, "list\n"))
                showlist();
            else if (!strcmp(cmdbuf, "help\n"))
                showhelp();
            getcmdline();
        }
    }
    else /* acts as server */
    {
        // 共享棧地址
        shmptr = (struct shmbuf *)shmat(shmid, 0, 0);
        // 設置信號量
        semid = semget(MY_SEMKEY, 1, IPC_CREAT | 0666);
        semval = 1;  // 賦初值爲1, 只允許一個訪問
        semctl(semid, 0, SETVAL, semval);
        // 退出
        signal(SIGINT, sigend);
        signal(SIGTERM, sigend);
        printf("NO OTHER OPERATION but press Ctrl+C or use kill to end.\n");
        shmptr->top = MAX_BLOCK - 1;  // 一開始棧滿
        for (int i = 0; i < MAX_BLOCK; i++)  // 棧裏順序放置一系列正整數
            shmptr->stack[i] = MAX_BLOCK - i;
        sleep(1000000); /* cause sleep forever. */
    }
}

void sigend(int sig)
{
    shmctl(shmid, IPC_RMID, 0);
    semctl(semid, IPC_RMID, 0);
    exit(0);
}

void relblock(void)
{
    if (local.top < 0)
    {
        printf("No block to release!");
        return;
    }
    semopbuf.sem_num = 0;
    semopbuf.sem_op = -1;
    semopbuf.sem_flg = SEM_UNDO;
    semop(semid, &semopbuf, 1);

    shmptr->top++;  // 移動共享棧的棧頂指針
    sleep(5);
    shmptr->stack[shmptr->top] = local.stack[local.top--];  // 共享棧壓入本地棧的一個塊號

    semopbuf.sem_num = 0;
    semopbuf.sem_op = 1;
    semopbuf.sem_flg = SEM_UNDO;
    semop(semid, &semopbuf, 1);
}

int getblock(void)
{
    semopbuf.sem_num = 0;
    semopbuf.sem_op = -1;
    semopbuf.sem_flg = SEM_UNDO;
    semop(semid, &semopbuf, 1);

    if (shmptr->top < 0)
    {
        printf("No free block to get!");
        return;
    }
    local.stack[++local.top] = shmptr->stack[shmptr->top];  // 共享棧彈出一個塊號到本地棧
    sleep(5);
    shmptr->top--;

    semopbuf.sem_num = 0;
    semopbuf.sem_op = 1;
    semopbuf.sem_flg = SEM_UNDO;
    semop(semid, &semopbuf, 1);
}

void showhelp(void)
{
    printf("\navailable COMMAND:\n\n");
    printf("help\tlist this help\n");
    printf("list\tlist all gotten block number\n");
    printf("get\tget a new block\n");
    printf("rel\trelease the last gotten block\n");
    printf("end\texit this program\n");
}

void showlist(void)
{
    int i;
    printf("List all gotten block number:\n");
    for (i = 0; i <= local.top; i++)
        printf("%d\t", local.stack[i]);
}

void getcmdline(void)
{
    printf("\n?> ");
    fgets(cmdbuf, MAX_CMD - 1, stdin);
}

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