實驗內容
臨界資源是一個建立在共享存儲區的棧
- 服務進程 建立並初始化棧, 初始狀態下共享棧滿,裏面順序放置一系列正整數(自棧頂向下: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);
}