实验三 进程同步实验

更新时间:2023-11-12 23:45:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

实验三:进程同步实验

实验学时:3学时

一、实验目的

1、掌握操作系统的进程同步原理 2、熟悉linux的进程同步原语 3、设计程序,实现经典进程同步问题 二、实验基本原理

1、在计算机操作系统中,PV操作是进程管理中的难点。

首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下: P(S):①将信号量S的值减1,即S=S?1;

②如果S?0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。 V(S):①将信号量S的值加1,即S=S+1;

②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。

PV操作的意义:我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信。

什么是信号量?信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。

一般来说,信号量S?0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S?0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。

2、linux的进程同步原语

(1)wait();阻塞父进程,子进程执行 (2)#include

#include

key_t ftok (char*pathname, char proj); 它返回与路径pathname相对应的一个键值。

(3)int semget(key_t key, int nsems, int semflg)

参数key是一个键值,由ftok获得,唯一标识一个信号灯集,用法与msgget()中的key相同;参数nsems指定打开或者新创建的信号灯集中将包含信号灯的数目;semflg参数是一些标志位。参数key和semflg的取值,以及何时打开已有信号灯集或者创建一个新的信号灯集与msgget()中的对应部分相同,不再祥述。该调用返回与健值key相对应的信号灯集描述字。调用返回:成功返回信号灯集描述字,否则返回-1。

注:如果key所代表的信号灯已经存在,且semget指定了IPC_CREAT|IPC_EXCL标志,那么即使参数nsems与原来信号灯的数目不等,返回的也是EEXIST错误;如果semget只指定了IPC_CREAT标志,那么参数nsems必须与原来的值一致,在后面程序实例中还要进一步说明。 (4)int semop(int semid, struct sembuf *sops, unsigned nsops);

semid是信号灯集ID,sops指向数组的每一个sembuf结构都刻画一个在特定信号灯上的操作。nsops为sops指向数组的大小。

(5)int semctl(int semid,int semnum,int cmd,union semun arg)

该系统调用实现对信号灯的各种控制操作,参数semid指定信号灯集,参数cmd指定具体的操作类型;参数semnum指定对哪个信号灯操作,只对几个特殊的cmd操作有意义;arg用于设置或返回信号灯信息。

该系统调用详细信息请参见其手册页,这里只给出参数cmd所能指定的操作。 IPC_STAT 获取信号灯信息,信息由arg.buf返回; IPC_SET

设置信号灯信息,待设置信息保存在arg.buf中(在manpage中给出了可以设置哪些信息);

GETALL 返回所有信号灯的值,结果保存在arg.array中,参数sennum被忽略; GETNCNT

返回等待semnum所代表信号灯的值增加的进程数,相当于目前有多少进程在等待semnum代表的信号灯所代表的共享资源;

GETPID 返回最后一个对semnum所代表信号灯执行semop操作的进程ID; GETVAL 返回semnum所代表信号灯的值;

GETZCNT 返回等待semnum所代表信号灯的值变成0的进程数; SETALL

通过arg.array更新所有信号灯的值;同时,更新与本信号集相关的semid_ds结构的sem_ctime成员;

SETVAL 设置semnum所代表信号灯的值为arg.val;

三、读者写者参考程序 #include #include #include #include #include #include #include #include

#define SEMKEY (key_t)0x200 typedef union _senum{ int val;

struct semid_ds *buf; ushort *array; }semun;

int semid; int count=0; FILE *fp,*fp1,*fp2;

struct sembuf prmutex={0,-1,0},pwmutex={1,-1,0},ps={2,-1,0};//第一个是索引量,第二个-1是p操作,1是v操作。

struct sembuf vrmutex={0,1,0},vwmutex={1,1,0},vs={2,1,0}; int initsem() /*信号量初始化*/

{ semun x; x.val=1;

if((semid=semget(SEMKEY,3,0600|IPC_CREAT|IPC_EXCL))==-1) {

if(errno==EEXIST)

semid=semget(SEMKEY,3,0); }

if(semctl(semid,0,SETVAL,x)==-1) {

perror(\ return(-1); }

if(semctl(semid,1,SETVAL,x)==-1) {

perror(\ return(-1); }

if(semctl(semid,2,SETVAL,x)==-1) {

perror(\ return(-1); }

return(semid); }

main() /*主操作*/ {int i,j,k; int a[30];

for(i=0;i<30;i++) {

a[i]=i; }

semid=initsem(); if(fork()==0) {

//reader

semop(semid,&prmutex,1);/*读操作p操作*/

if(count==0) semop(semid,&pwmutex,1);/*堵塞写进程*/ count=count+1;/*读者数*/ printf(\

semop(semid,&vrmutex,1); for(i=0;i<30;i++) {

printf(\ sleep(1); }

semop(semid,&prmutex,1); count=count-1;

printf(\

if(count==0) semop(semid,&vwmutex,1);/*唤醒写进程*/ semop(semid,&vrmutex,1); exit(0); } else {

if(fork()==0) { //writer

semop(semid,&pwmutex,1); printf(\ for(k=0;k<30;k++) {

a[k]=3*k; //sleep(1); }

printf(\ semop(semid,&vwmutex,1); exit(0); } else

{ if(fork()==0) //reader {

semop(semid,&prmutex,1);/*读操作p操作*/

//if(count==0) semop(semid,&pwmutex,1);/*堵塞写进程*/ //printf(\ count=2;/*读者数*/

printf(\ semop(semid,&vrmutex,1); for(j=0;j<30;j++) {

printf(\ sleep(1); }

semop(semid,&prmutex,1); count=count-1;

printf(\

if(count==0) semop(semid,&vwmutex,1);/*唤醒写进程*/ semop(semid,&vrmutex,1); exit(0); } } } }

本文来源:https://www.bwwdw.com/article/3kqv.html

Top