Linux进程互斥——临界资源访问
模拟临界资源访问的示例程序
本示例的临界资源是一个建立在共享存储区的栈,由服务进程建立并初始化。初始状态下共享栈满,里面顺序放置一系列正整数(自栈顶向下:1,2,3…),代表空闲块号。客户进程利用共享栈进行数据块的分配和释放,以得到、归还一个块号代表,并不进行任何后续操作。程序中getblock过程从共享栈中弹出一个块号(分配),relblock过程把一个已分配块号压入共享栈(释放)。为简单起见,已分配块号在本地也使用栈结构保存,因而每次释放的是最后分配的块号。
编译后执行,第一个进程实例将作为服务进程,提示:
NO OTHER OPERATION but press Ctrl+C or use kill to end.
服务进程完成初始化后将进入睡眠状态,直到用户按Ctrl+C终止执行,或使用kill命令杀死服务进程。
其他进程实例作为客户进程,进入后首先有命令帮助提示,然后显示命令提示符“?> ”,在命令提示下可以使用的命令包括:
help 显示可用命令
list 列出所有已分配块号
get 分配一个新块
rel 释放最后分配块号
end 退出程序
示例程序代码
#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 // need to change
#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
"))
{
if(!strcmp(cmdbuf,"get
"))
getblock();
else if(!strcmp(cmdbuf,"rel
"))
relblock();
else if(!strcmp(cmdbuf,"list
"))
showlist();
else if(!strcmp(cmdbuf,"help
"))
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.
");
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++;
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];
shmptr->top--;
}
void showhelp(void)
{
printf("
available COMMAND:
");
printf("help list this help
");
printf("list list all gotten block number
");
printf("get get a new block
");
printf("rel release the last gotten block
");
printf("end exit this program
");
}
void showlist(void)
{
int i;
printf("List all gotten block number:
");
for(i=0; i<=local.top; i++)
printf("%%d ", local.stack[i]);
}
void getcmdline(void)
{
printf("
?> ");
fgets(cmdbuf, MAX_CMD-1, stdin);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
运行结果
结果分析:
运行程序,第一个进程实例作为服务进程,第二个进程实例作为客户进程,然后可以在命令提示符下输入命令,分配或释放一个新块,列出已分配的块等,并未发现程序出错的情况。(程序其实是存在错误的)
改造程序,使错误易于观察
为了使错误易于观察,改进分配和释放数据块的函数relblock(void)和 getblock(void)
代码如下
void relblock(void)
{
if(local.top<0)
{
printf("No block to release!");
return;
}
shmptr->top++;
sleep(10);
shmptr->stack[shmptr->top]=local.stack[local.top--];
}
void getblock(void)
{
if(shmptr->top<0)
{
printf("No free block to get!");
return;
}
local.stack[++local.top]=shmptr->stack[shmptr->top];
sleep(10);
shmptr->top--;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
运行结果
结果分析
可以看到,两个客户进程都申请数据块时,两个进程都得到数据块1,而一个数据块只能分配给一个进程,而使得数据块2丢失
利用信号量机制实现进程互斥功能
利用信号量sign来实现同时执行getblock(),即申请数据块。或者先执行relblock()再执行getblock() 即,先申请数据块再申请数据块。的互斥。同理有relblock() 和getblock(),relblock()和getblock()也会出现类似错误。
为了方面实现,在上一个代码基础上,在结构shmbuf的定义中添加了信号量sign的定义。之后在代码中进行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 // need to change
#define MAX_BLOCK 1024
#define MAX_CMD 8
//共享存储区的栈结构
//为简单起见,已分配块号在本地也使用栈结构local保存
struct shmbuf
{
int top;
int stack[MAX_BLOCK];
int sign;
} *shmptr, local;
//
char cmdbuf[MAX_CMD];
int shmid, semid;
void sigend(int);
void relblock(void);//释放最后分配块号
void 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)
{
//获得共享存储区首地址
shmid=shmget(MY_SHMKEY, sizeof(struct shmbuf), 0666);
//把一个共享存储区附接到进程内存空间;
shmptr=(struct shmbuf *)shmat(shmid, 0, 0);
//初始化
local.top=-1;
//输出命令提示
showhelp();
//接受输入命令
getcmdline();
while(strcmp(cmdbuf,"end
"))
{
if(!strcmp(cmdbuf,"get
"))
{
while(shmptr->sign==0);
shmptr->sign=0;
getblock();
shmptr->sign=1;
}
else if(!strcmp(cmdbuf,"rel
"))
{
while(shmptr->sign==0);
shmptr->sign=0;
relblock();
shmptr->sign=1;
}
else if(!strcmp(cmdbuf,"list
"))
showlist();
else if(!strcmp(cmdbuf,"help
"))
showhelp();
//接受输入命令
getcmdline();
}
}
else
{
int i;
//把一个共享存储区附接到进程内存空间;
shmptr=(struct shmbuf *)shmat(shmid, 0, 0);
shmptr->sign=1;
//设置对信号的处理方式或处理过程
signal(SIGINT, sigend);
signal(SIGTERM, sigend);
printf("NO OTHER OPERATION but press Ctrl+C or use kill to end.
");
//初始状态下共享栈满,里面顺序放置一系列正整数(自栈顶向下:,2,3...),
//代表空闲块号
shmptr->top=MAX_BLOCK-1;//shmptr->top值越大,可利用物理块就越多
for(i=0; i<MAX_BLOCK; i++)
shmptr->stack[i]=MAX_BLOCK-i;
sleep(1000000);
}
}
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(10);//便于观察错误
shmptr->stack[shmptr->top]=local.stack[local.top--];
}
void getblock(void)
{
if(shmptr->top<0)
{
printf("No free block to get!");
return;
}
local.stack[++local.top]=shmptr->stack[shmptr->top];
sleep(10);//便于观察错误
shmptr->top--;
}
//显示可用命令
void showhelp(void)
{
printf("
available COMMAND:
");
printf("help list this help
");
printf("list list all gotten block number
");
printf("get get a new block
");
printf("rel release the last gotten block
");
printf("end exit this program
");
}
//列出所有已分配块号
void showlist(void)
{
int i;
printf("List all gotten block number:
");
for(i=0; i<=local.top;i++)
printf("%%d ",local.stack[i]);
}
//接受输入命令
void getcmdline(void)
{
printf("
?> ");
fgets(cmdbuf, MAX_CMD-1, stdin);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
运行结果
结果分析
增加了信号量机制后,可以看到两个客户进程同时申请数据块并不会出错,也没有造成数据块丢失。即通过信号量机制,实现了两个客户进程之间的互斥