操作系统 实验四 使用信号量进行互斥和同步
实验四 使用信号量进行互斥和同步实验目的本实验介绍在Linux中使用信号量进行进程同步、互斥的方法。读者可以通过实验进一步理解进程间同步与互斥、临界区与临界资源的概念与含义,并学会Linux信号量的基本使用方法。实验环境硬件环境:计算机一台,局域网环境;软件环境:Linux Ubuntu操作系统,gcc编译器。实验内容和步骤(一)参考:POSIX以及System VSystem V:Unix众多版
实验四 使用信号量进行互斥和同步
实验目的
本实验介绍在Linux中使用信号量进行进程同步、互斥的方法。读者可以通过实验进一步理解进程间同步与互斥、临界区与临界资源的概念与含义,并学会Linux信号量的基本使用方法。
实验环境
硬件环境:计算机一台,局域网环境;
软件环境:Linux Ubuntu操作系统,gcc编译器。
实验内容和步骤
- (一)参考:POSIX以及System V
-
System V:Unix众多版本中的一支,最初由AT&T定义,目前为第四个版本,其中定义了较为复杂的API。
-
POSIX:Portable Operating System Interface,IEEE为了统一Unix接口而定义的标准,定义了统一的API接口。Linux即支持System API,又支持POSIX API
- (二)Linux提供两种信号量:
-
内核信号量:用于内核中资源共享控制
-
用户态信号量:主要包括POSIX信号量和SYSTEM V信号量。其中
-
POSIX信号量分为两类。
-
无名信号量:主要用于线程间同步,也可用于进程间(由fork产生)同步。
-
有名信号量:既可用于进程间同步,也可用于线程间同步。
-
POSIX有名信号量主要包括:
-
如果有任何进程/线程引用这个信号量,sem_unlink函数不会起到任何作用,即只有最后一个使用该信号量的进程来执行sem_unlink才有效。
-
(三)实验步骤
-
step1:通过实例查看不使用互斥时的情况
/* * @Description: 观察没有互斥的情况下 * @version: * @Author: * @Date: 2021-04-30 10:48:37 * @LastEditors: Please set LastEditors * @LastEditTime: 2021-04-30 10:52:47 */ #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { char message = 'X'; int i = 0; if (argc > 1) message = argv[1][0]; for (i = 0; i < 10; i++) { printf("%c", message); fflush(stdout); sleep(rand() % 3); printf("%c", message); fflush(stdout); sleep(rand() % 2); } sleep(10); exit(0); }
-
-
观察X和O的出现规律,并分析原因。
-
出现无规律,因为两个进程并发执行,并没有进行互斥同步
-
step2:使用信号量来对临界资源进行互斥
/* * @Description: 利用信号量进行互斥访问 * @version: * @Author: * @Date: 2021-04-30 12:29:46 * @LastEditors: Please set LastEditors * @LastEditTime: 2021-04-30 12:39:48 */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/stat.h> #include <semaphore.h> #include <fcntl.h> int main(int argc, char *argv[]) { char message = 'X'; int i = 0; if (argc > 1) message = argv[1][0]; sem_t *mutex = sem_open("mysem", O_CREAT, 0666, 1); for (i = 0; i < 10; ++i) { // P操作 sem_wait(mutex); printf("%c", message); fflush(stdout); sleep(rand() % 3); printf("%c", message); fflush(stdout); // V操作 sem_post(mutex); sleep(rand() % 2); } sleep(10); sem_close(mutex); sem_unlink("mysem"); exit(0); }
-
编译链接,同时运行两个进程。
-
观察X和O的出现规律,并分析原因。
-
一定是偶数个X后跟着相同偶数个的O,因为在循环开始应用了信号量,进行了互斥,即使在输出第二个字符前sleep但是信号量没有打开,则第二个进程也不能输出,第二个进程阻塞,只有当输出偶数个X后,执行了V操作才能允许第二个进程输出。这样就造成了只有偶数个的情况。
-
-
step3:使用信号量来模拟下象棋红黑轮流走子的情况
-
编写两个C语言程序black_chess.c以及red_chess.c,分别模拟下象棋过程中红方走子和黑方走子过程。
-
走子规则:红先黑后,红、黑双方轮流走子,到第10步,红方胜,黑方输。
-
编程思路:设置一下两个同步信号量。
(1)hei:初值为1,代表黑方已经走子,轮到红方走子(满足棋规“红先黑后”)。
(2)hong: 初值为0,代表红方尚未走子。
-
红棋走子之前,先测试信号量hei,判断黑方是否已经走子,如是,则轮到红方走子,否则阻塞等待黑子走子,由于hei的初值为1,因此一定是红方先走。红方走子完毕后,置信号量hong,通知黑方走子。
-
黑方走子之前,先测试信号量hong,判断红方是否已经走子,如是,则轮到黑方走子,否则阻塞等待红方走子,由于hong的初值为0,因此在红方没有走子之前,黑方不会走子。黑方走子完毕后,置信号量hei,通知红方走子。
/* * @Description: 走红棋程序 * @version: * @Author: * @Date: 2021-04-30 12:49:34 * @LastEditors: Please set LastEditors * @LastEditTime: 2021-04-30 12:56:54 */ // 红棋先走,10步,红色赢 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/stat.h> #include <fcntl.h> #include <semaphore.h> int main(int argc, char *argv[]) { int i = 0; // 黑棋是否已经走过,是则是1,初始值是1,因为让红棋先走 sem_t *hei = sem_open("chess_black_sem", O_CREAT, 0666, 1); sem_t *hong = sem_open("chess_red_sem", O_CREAT, 0666, 0); for (i = 0; i < 10; i++) { sem_wait(hei); if (i != 9) printf("Red chess had moved, black chess go!\n"); else printf("Red chess win!!!\n"); fflush(stdout); sem_post(hong); } sleep(10); sem_close(hei); sem_close(hong); sem_unlink("chess_red_sem"); sem_unlink("chess_black_sem"); exit(0); }
/* * @Description: 走黑棋程序 * @version: * @Author: * @Date: 2021-04-30 12:58:04 * @LastEditors: Please set LastEditors * @LastEditTime: 2021-04-30 13:02:42 */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ipc.h> #include <fcntl.h> #include <semaphore.h> int main(int argc, char *argv[]) { int i = 0; sem_t *hei = sem_open("chess_black_sem", O_CREAT, 0666, 1); sem_t *hong = sem_open("chess_red_sem", O_CREAT, 0666, 0); for (i = 0; i < 10; i++) { sem_wait(hong); if (i != 9) printf("Black chess had moved, red chess go!\n"); else printf("Black chess lost!!!\n"); fflush(stdout); sem_post(hei); } sleep(10); sem_close(hei); sem_close(hong); sem_unlink("chess_red_sem"); sem_unlink("chess_black_sem"); exit(0); }
-
四、实验总结
- 对程序结果进行分析,并结合操作系统课程中讲授的原理,总结信号量在进程同步和互斥中所起的作用。
-
信号量的应用可以解决多个进程同时访问某一临界资源的互斥问题。当进程检测到信号量为<=0时,说明已经没有临界资源,将阻塞等待其他进程释放资源(信号量加一),这样就实现了互斥关系。
-
同样也可以解决多个进程的前驱关系中,多个进程的同步问题。如上面的下棋问题,同步关系是,只有当对方已经下了自己才能下,这样一个前驱(合作)关系。用信号量实现就是在自己下棋前检查对放信号量,是否为1,为1表示已经下了,自己可以下。如果这时切换到另一个进程,检查对方的信号量,这时对方信号量仍为0 ,表示还没有下,所以应该阻塞等待。在下棋后,将自己的信号量置1,表示自己已经下了,唤醒对方进程。如此就实现了进程的同步。
-
对信号量的操作如果没有成对出现,会导致什么现象发生?
-
实现进程互斥时,如果缺少P操作则不能保证对临界资源的互斥访问,如果缺少V操作则会使临界资源得不到释放,从而使因等待该资源而阻塞的进程不能被唤醒。可能会造成死锁。
-
实现进程同步时,如S1->s2,如果缺少P操作不能s2一定在s1,之前进行,如果缺少V操作,则导致S2阻塞后而无法唤醒,无法执行。
-
-
用于实现同步、互斥的信号量再出现的位置上各有什么特点?
-
对于实现互斥的信号量:是在访问临界资源前进行P操作,访问完毕后执行V操作
-
对于实现同步的信号量:是在程序开始前进行对方信号量的P操作,结束后执行对自己信号量的V操作。(前P后V)
-
-
如果改成“黑先红后” ,红、黑双方轮流走子,到第20步,黑方胜,红方输,如何编程实现?
-
/* * @Description: 黑先红后,20步,黑赢 * @version: * @Author: * @Date: 2021-04-30 13:25:00 * @LastEditors: Please set LastEditors * @LastEditTime: 2021-04-30 13:35:46 */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/stat.h> #include <fcntl.h> #include <semaphore.h> int main(int argc, char *argv[]) { int i = 0; sem_t *hei = sem_open("chess_black_sem", O_CREAT, 0666, 0); sem_t *hong = sem_open("chess_red_sem", O_CREAT, 0666, 1); for (i = 0; i < 20; i++) { sem_wait(hei); if (i != 19) printf("Red chess had moved, black chess go!\n"); else printf("Red chess lost!!!\n"); fflush(stdout); sem_post(hong); } sleep(10); sem_close(hei); sem_close(hong); sem_unlink("chess_red_sem"); sem_unlink("chess_black_sem"); exit(0); }
/* * @Description: * @version: * @Author: * @Date: 2021-04-30 13:26:01 * @LastEditors: Please set LastEditors * @LastEditTime: 2021-04-30 13:35:40 */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ipc.h> #include <fcntl.h> #include <semaphore.h> int main(int argc, char *argv[]) { int i = 0; sem_t *hei = sem_open("chess_black_sem", O_CREAT, 0666, 0); sem_t *hong = sem_open("chess_red_sem", O_CREAT, 0666, 1); for (i = 0; i < 20; i++) { sem_wait(hong); if (i != 19) printf("Black chess had moved, red chess go!\n"); else printf("Black chess win!!!\n"); fflush(stdout); sem_post(hei); } sleep(10); sem_close(hei); sem_close(hong); sem_unlink("chess_red_sem"); sem_unlink("chess_black_sem"); exit(0); }
-
更多推荐
所有评论(0)