实验内容
临界资源是一个建立在共享存储区的栈
- 服务进程 建立并初始化栈, 初始状态下共享栈满,里面顺序放置一系列正整数(自栈顶向下: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);
}