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);
}

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值