经典的进程同步问题——临界资源访问

  • 模拟临界资源访问的示例程序
    • 示例程序代码
      • 运行结果
    • 改造程序,使错误易于观察
      • 代码如下
      • 运行结果
    • 利用信号量机制实现进程互斥功能
      • 示例代码
      • 运行结果

模拟临界资源访问的示例程序

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

运行结果


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

Linux进程互斥——临界资源访问相关推荐

  1. linux进程互斥要点,linux进程之间互斥

    总所周知,在linux中pthread_mutex_t可以用于同一进程内多个线程之间的同步.我们所需要做的工作,仅仅是定义一个全局的pthread_mutex_t类型变量即可.但是对于进程之间的互斥, ...

  2. Linux进程3——虚拟地址访问

    1.虚拟地址到物理地址 X86芯片访问内存,需要使用MMU功能,实现虚拟地址到线性地址,再到物理地址的访问. X86的分段机制是强制的,分页机制是可选的. 2.分段机制 X86的虚拟地址,由选择符:偏 ...

  3. Linux进程互斥——生产者-消费者

    经典的进程同步问题--生产者-消费者 模拟生产者-消费者的示例程序 示例程序代码 运行结果 改造程序,取消所有的同步机制,记录执行情况并进行分析 代码如下 运行结果 模拟生产者-消费者的示例程序 本示 ...

  4. 什么是临界资源计算机网络,如何利用信号量机制来实现多个进程对临界资源的互斥访问...

    进程互斥 定义:两个或两个以上的进程,不能同时进入关于同一组共享变量的临界区域,否则可能发生与时间有关的错误,这种现象被称作进程互斥. 在多道程序环境下,存在着临界资源,它是指多进程存在时必须互斥访问 ...

  5. linux 进程通信比较,浅析Linux进程通信的几种方式及其比较

    摘要:本文通过对Linux下几种主要通信方式进行概述,并结合Linux系统的系统调用对OS中的实现进行简要概述,并对其优缺点进行分析,阐述了在不同通信情况下应该选择何种通信方式进行选择. 关键词:Li ...

  6. Linux 进程 | 进程间的通信方式

    文章目录 管道 匿名管道 pipe 命名管道 FIFO 共享内存 共享内存的使用流程: 消息队列 信号量 套接字 在之前的博客中讲过,虚拟空间出现的其中一个目的就是解决 进程没有独立性,可能访问同一块 ...

  7. Linux进程通信的四种方式——共享内存、信号量、无名管道、消息队列|实验、代码、分析、总结

    Linux进程通信的四种方式--共享内存.信号量.无名管道.消息队列|实验.代码.分析.总结 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须 ...

  8. (大集合)Linux进程和线程的基本编程、通讯和例程【杂记】

    Linux 进程 和 线程 的基本 编程.通讯 和 例程 注:[杂记]系列为日常网搜资料的堆砌而积累成之.如有错误恭谢指出.标识为"原创"其实不完全是,只是多引用再整理和加上自己的 ...

  9. linux内核互斥锁

    Linux内核互斥锁–mutex 一.定义: /linux/include/linux/mutex.h 二.作用及访问规则: 互斥锁主要用于实现内核中的互斥访问功能.内核互斥锁是在原子 API 之上实 ...

最新文章

  1. mysql的query cache_MySQL 缓存 Query Cache
  2. 大家放松下吧,咱家先在首页呆会儿!
  3. 编译安装Apache2.4.10
  4. RH124 第六单元   管理物理存储
  5. 爬虫 spider02——详析http
  6. python空类型用什么表示_python中怎么表示空值
  7. Java中的装饰器设计模式
  8. SAP License:SAP如何区分固定成本和变动成本
  9. Linux 命令(0)—— man 命令
  10. 笔记本电脑性价比排行2019_笔记本电脑性价比排行2020榜单介绍
  11. 并发编程常见面试题总结五
  12. windows XP系统内核文件分析精简系统很有好处
  13. 【Tips小技巧】电脑全屏截图网页滚动截图
  14. 利用计算机本地文档重装系统,本地模式怎么进入PE系统对电脑进行重装
  15. 量子计算进阶:量子计算机的组建和量子计算原理(包含相关论文推荐60篇)
  16. 计算机桌面的图标怎么删除,桌面图标有蓝底怎么去掉,教您去掉电脑桌面图标蓝底的方法...
  17. 【AAAI 2021】多出口架构的知识蒸馏:Harmonized Dense Knowledge Distillation Training for Multi-Exit Architectures
  18. 无人巴士和无人出租车都能用的L4自动驾驶通用硬件方案
  19. CloudComparePCL 基于FPFH特征的SAC-IA算法
  20. 【VBA】 将VBA代码插入Excel中

热门文章

  1. 可道云源码php,Kodexplorer可道云 v4.25
  2. 经典chrony服务和ssh远程连接的练习
  3. suricata 各个线程干的事情 -- 主线程
  4. css div网页布局代码 自适应,css+div页面布局之1 -- 自适应
  5. 将一个数组分成和尽可能相等的两份
  6. 微信小程序——引入vant有赞组件 2022年3月3日
  7. 集合对象数据展示到前端页面
  8. python学习:三目运算符
  9. 解决CUDA程序的黑屏恢复问题
  10. IMX6ULL学习笔记(四) —— uboot 启动流程