操作系统实验四 使用信号量进行互斥与同步

一、实验目的

本实验介绍在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_t* sem_open(const char *name, int oflag, mode_t mode, int value)

    • name: 文件名路径,如’mysem’,会创建/dev/shm/sem.mysem
    • oflag:O_CREATE或O_CREATE | O_EXCL
      • O_CREATE:如信号量存在,则打开之,如不存在,则创建 
      • O_CREATE | O_EXCL:如信号量已存在,则返回error
    • mode:信号量访问权限,如0666
    • value:信号量初始化值 

      int sem_wait(sem_t *sem)

    • 测试指定的信号量的值,相当于P操作 
    • 若sem > 0,则减1立刻返回; 
    • 若sem = 0,则睡眠直到sem > 0,此时立刻减1,然后返回 
    • int sem_post(sem_t *sem)
    • 释放资源,相当于V操作 
    • 信号量sem的值加1
    • 唤醒正在等待该信号量的进程/线程 

int sem_close(sem_t *sem)

    • 关闭有名信号量 
    • 进程中,如果使用完信号量,应使用该函数关闭有名信号量 
    • int sem_unlink(const char *name)
    • 删除系统中的信号量 

如果有任何进程/线程引用这个信号量,sem_unlink函数不会起到任何作用,即只有最后一个使用该信号量的进程来执行sem_unlink才有效。

(三)实验步骤

step1:通过实例查看不使用互斥时的情况 (文件命名为no_sem.c)

程序代码:

#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);
}

编译链接,同时运行两个进程,显示结果

操作系统实验四 使用信号量进行互斥与同步

step2:使用信号量来对临界资源进行互斥 (文件命名为with_sem.c)

程序代码:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<semaphore.h>
#include<fcntl.h>
#include<sys/stat.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++){
		sem_wait(mutex);
		printf("%c", message);
		fflush(stdout);
		sleep(rand()%3);
		printf("%c", message);
		fflush(stdout);
		sem_post(mutex);
		sleep(rand()%2);
	}
	sleep(10);
	sem_close(mutex);
	sem_unlink("mysem");
	exit(0);
}

运行结果:

操作系统实验四 使用信号量进行互斥与同步

观察X和O的出现规律,并分析原因:

在不使用互斥的程序中,X和O的出现没有什么规律,在使用信号量来对临界资源进行互斥时,X和O以相同的数目交替出现。因为前者发生了进程同步,并发执行的进程因直接制约关系而需相互等待,相互合作,以实现各进程按相互协调的速度向前推进。后者发生了进程互斥,因间接制约而导致进程交替执行。

通过使用信号量,可以实现多个进程对临界资源的互斥访问,从而解决互斥问题,可以实现进程或语句之间的前趋关系,即实现进程之间的直接制约关系,从而解决同步问题。

step3:使用信号量来模拟下象棋红黑轮流走子的情况

编写两个C语言程序black_chess.c以及red_chess.c,分别模拟下象棋过程中红方走子和黑方走子过程。走子规则:红先黑后,红、黑双方轮流走子,到第10步,红方胜,黑方输。

编程思路:

设置一下两个同步信号量。

(1)hei:初值为1,代表黑方已经走子,轮到红方走子(满足棋规“红先黑后”)。

(2)hong: 初值为0,代表红方尚未走子。

红棋走子之前,先测试信号量hei,判断黑方是否已经走子,如是,则轮到红方走子,否则阻塞等待黑子走子,有于hei的初值为1,因此一定是红方先走。红方走子完毕后,置信号量hong,通知黑方走子。

黑方走子之前,先测试信号量hong,判断红方是否已经走子,如是,则轮到黑方走子,否则阻塞等待红方走子,由于hong的初值为0,因此在红方没有走子之前,黑方不会走子。黑方走子完毕后,置信号量hei,通知红方走子。

程序代码:

//red_chess.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<semaphore.h>
#include<fcntl.h>
#include<sys/stat.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(hei);
		if(i != 9){
			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);
}
//black_chess.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<semaphore.h>
#include<fcntl.h>
#include<sys/stat.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);
}

同时执行两个进程,运行结果:

操作系统实验四 使用信号量进行互斥与同步

四、实验总结

对程序结果进行分析,并结合操作系统课程中讲授的原理,总结信号量在进程同步和互斥中所起的作用。

思考:

(1)对信号量的操作如果没有成对出现,会导致什么现象发生?

实现进程互斥时,如果缺少P操作则不能保证对临界资源的互斥访问,如果缺少V操作则会使临界资源得不到释放,从而使因等待该资源而阻塞的进程不能被唤醒。

实现进程同步时,如S1->s2,如果缺少P操作不能s2一定在s1,之前进行,如果缺少V操作,则S2无法执行。

(2)用于实现同步、互斥的信号量再出现的位置上各有什么特点?

都是成对出现,实现互斥的信号量成对的P原语在V原语之前,实现同步的信号量成对的P原语在V原语之后。

(3)如果改成“黑先红后” ,红、黑双方轮流走子,到第20步,黑方胜,红方输,如何编程实现?

程序代码:

//red_chess.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<semaphore.h>
#include<fcntl.h>
#include<sys/stat.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 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);
}
//black_chess.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<semaphore.h>
#include<fcntl.h>
#include<sys/stat.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);
}

运行结果:

操作系统实验四 使用信号量进行互斥与同步

 

上一篇:vbs脚本和windows定时任务实现qq消息表情包定时发送


下一篇:Linux高性能服务器编程学习记录——十三、多进程编程