WHCSRL 技术网

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

运行结果

在这里插入图片描述
结果分析
增加了信号量机制后,可以看到两个客户进程同时申请数据块并不会出错,也没有造成数据块丢失。即通过信号量机制,实现了两个客户进程之间的互斥

推荐阅读