linux网络编程之System V 信号量(二):用信号量实现进程互斥示例和解决哲学家就餐问题

本文主要是介绍linux网络编程之System V 信号量(二):用信号量实现进程互斥示例和解决哲学家就餐问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、我们在前面讲进程间通信的时候提到过进程互斥的概念,下面写个程序来模拟一下,程序流程如下图:
这里写图片描述
即父进程打印字符O,子进程打印字符X,每次打印一个字符后要sleep 一下,这里要演示的效果是,在打印程序的边界有PV操作,故每个进程中间sleep 的时间即使时间片轮转到另一进程,由于资源不可用也不会穿插输出其他字符,也就是说O或者X字符都会是成对出现的,如OOXXOOOOXXXXXXOO….
程序如下:

#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/wait.h>#define ERR_EXIT(m) \do { \perror(m); \exit(EXIT_FAILURE); \} while(0)union semun
{int val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
};int semid;
/* pv操作之间的临界区,导致打印的字符一定是成对出现的 */
void print(char op_char)
{int pause_time;srand(getpid());int i;for (i = 0; i < 10; i++){sem_p(semid);printf("%c", op_char);fflush(stdout);pause_time = rand() % 3;sleep(pause_time);printf("%c", op_char);fflush(stdout);sem_v(semid);pause_time = rand() % 2;sleep(pause_time);}}int main(void)
{semid = sem_create(IPC_PRIVATE);sem_setval(semid, 1);pid_t pid;pid = fork();if (pid == -1)ERR_EXIT("fork");if (pid > 0){print('o');wait(NULL);sem_d(semid);}else{print('x');}return 0;
}

sem_create 等函数参考工具集。在调用semget 时指定key = IPC_PRIVATE,表示创建的是私有的信号量集,但具有亲缘关系的进程是可见的,比如父子进程。输出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./print
ooxxooxxooxxooxxooooooxxooxxooxxooxxxxxx
可以看到输出都是成对出现的字符。
分析一下:semval = 1,假设父进程先被调度执行,父进程先P了一下,此时 semval = 0,子进程在父进程睡眠时间被调度的时候尝试P,semval = -1,然后子进程阻塞了,父进程打印完V了一下,semval = 0,唤醒子进程,子进程的P操作返回,打印字符睡眠后V了一下,semval = 1。当然在子进程睡眠的时候父进程可能也在尝试P,故就一直循环往复下去。

二、哲学家就餐问题的描述可以参考这里,下面我们尝试解决这个问题的方法是:仅当一个哲学家两边筷子都可用时才允许他拿筷子。
这里写图片描述
上图中红色数字表示哲学家的编号,总共5个哲学家,用5个进程来表示;黑色数字表示筷子的编号,总共有5根筷子,可以定义一个信号量集中含有5个信号量,每个信号量的初始值为1,当某个哲学家可以同时得到两根筷子(同时P两个信号量返回)时可以用餐,否则阻塞等待中。用餐后需要同时V一下两个信号量,让其他进程可以P成功。
程序如下:

#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/wait.h>#define ERR_EXIT(m) \do { \perror(m); \exit(EXIT_FAILURE); \} while(0)union semun
{int val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
};int semid;#define DELAY (rand() % 5 + 1)void wait_for_2fork(int no)
{int left = no;int right = (no + 1) % 5;struct sembuf buf[2] ={{left, -1, 0},{right, -1, 0}};semop(semid, buf, 2);
}void free_2fork(int no)
{int left = no;int right = (no + 1) % 5;struct sembuf buf[2] ={{left, 1, 0},{right, 1, 0}};semop(semid, buf, 2);
}void philosopere(int no)
{srand(getpid());for (; ;){printf("%d is thinking\n", no);sleep(DELAY);printf("%d is hungry\n", no);wait_for_2fork(no);printf("%d is eating\n", no);sleep(DELAY);free_2fork(no);}
}int main(void)
{semid = semget(IPC_PRIVATE, 5, IPC_CREAT | 0666);if (semid == -1)ERR_EXIT("semget");union semun su;su.val = 1;int i;for (i = 0; i < 5; i++){semctl(semid, i, SETVAL, su);}int no = 0;pid_t pid;for (i = 1; i < 5; i++){pid = fork();if (pid == -1)ERR_EXIT("fork");if (pid == 0){no = i;break;}}philosopere(no);return 0;
}

我们在前面说过,当需要对一个信号量集中的多个信号量操作时,要么全部执行,要么全部不执行,即是一个原子操作,某个进程需要等待两根筷子,即对两个信号量同时P成功才可以用餐,信号量的序号是0~4,可看作筷子的编号,此时semop 函数操作的是2个信号量,即需定义2个struct sembuf 结构体成员的数组 struct sembuf buf[2];
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./dinning
0 is thinking
3 is thinking
2 is thinking
4 is thinking
1 is thinking
4 is hungry
4 is eating
0 is hungry
3 is hungry
1 is hungry
1 is eating
2 is hungry
3 is eating
4 is thinking
1 is thinking
0 is eating
4 is hungry
0 is thinking
1 is hungry
1 is eating
3 is thinking
4 is eating
0 is hungry
1 is thinking
2 is eating
0 is eating
4 is thinking
2 is thinking
1 is hungry
3 is hungry
3 is eating
0 is thinking
2 is hungry
1 is eating
4 is hungry
…………….
如果发现程序没有运行卡着,即没有发生死锁现象,从中也可以发现同时最多只能有两个哲学家一起用餐,也不会出现相邻哲学家一起用餐的情况。

参考:
《UNP》

转载自http://blog.csdn.net/jnu_simba/article/details/9101581

这篇关于linux网络编程之System V 信号量(二):用信号量实现进程互斥示例和解决哲学家就餐问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/300485

相关文章

Linux中shell解析脚本的通配符、元字符、转义符说明

《Linux中shell解析脚本的通配符、元字符、转义符说明》:本文主要介绍shell通配符、元字符、转义符以及shell解析脚本的过程,通配符用于路径扩展,元字符用于多命令分割,转义符用于将特殊... 目录一、linux shell通配符(wildcard)二、shell元字符(特殊字符 Meta)三、s

Linux之软件包管理器yum详解

《Linux之软件包管理器yum详解》文章介绍了现代类Unix操作系统中软件包管理和包存储库的工作原理,以及如何使用包管理器如yum来安装、更新和卸载软件,文章还介绍了如何配置yum源,更新系统软件包... 目录软件包yumyum语法yum常用命令yum源配置文件介绍更新yum源查看已经安装软件的方法总结软

linux报错INFO:task xxxxxx:634 blocked for more than 120 seconds.三种解决方式

《linux报错INFO:taskxxxxxx:634blockedformorethan120seconds.三种解决方式》文章描述了一个Linux最小系统运行时出现的“hung_ta... 目录1.问题描述2.解决办法2.1 缩小文件系统缓存大小2.2 修改系统IO调度策略2.3 取消120秒时间限制3

Linux alias的三种使用场景方式

《Linuxalias的三种使用场景方式》文章介绍了Linux中`alias`命令的三种使用场景:临时别名、用户级别别名和系统级别别名,临时别名仅在当前终端有效,用户级别别名在当前用户下所有终端有效... 目录linux alias三种使用场景一次性适用于当前用户全局生效,所有用户都可调用删除总结Linux

Linux:alias如何设置永久生效

《Linux:alias如何设置永久生效》在Linux中设置别名永久生效的步骤包括:在/root/.bashrc文件中配置别名,保存并退出,然后使用source命令(或点命令)使配置立即生效,这样,别... 目录linux:alias设置永久生效步骤保存退出后功能总结Linux:alias设置永久生效步骤

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

关于@MapperScan和@ComponentScan的使用问题

《关于@MapperScan和@ComponentScan的使用问题》文章介绍了在使用`@MapperScan`和`@ComponentScan`时可能会遇到的包扫描冲突问题,并提供了解决方法,同时,... 目录@MapperScan和@ComponentScan的使用问题报错如下原因解决办法课外拓展总结@