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\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++;
    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("\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);
}

运行结果

在这里插入图片描述

结果分析:
运行程序,第一个进程实例作为服务进程,第二个进程实例作为客户进程,然后可以在命令提示符下输入命令,分配或释放一个新块,列出已分配的块等,并未发现程序出错的情况。(程序其实是存在错误的)

改造程序,使错误易于观察

为了使错误易于观察,改进分配和释放数据块的函数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丢失

利用信号量机制实现进程互斥功能

利用信号量sign来实现同时执行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\n"))
       {
           if(!strcmp(cmdbuf,"get\n"))
           {
               while(shmptr->sign==0);           
               shmptr->sign=0;
               getblock();
               shmptr->sign=1;
           }
           else if(!strcmp(cmdbuf,"rel\n"))
           {
               while(shmptr->sign==0);      
               shmptr->sign=0;
               relblock();
               shmptr->sign=1;
           }
           else if(!strcmp(cmdbuf,"list\n"))
               showlist();
           else if(!strcmp(cmdbuf,"help\n"))
               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.\n");
       //初始状态下共享栈满,里面顺序放置一系列正整数(自栈顶向下:,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("\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);
}

运行结果

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

  • 9
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值