哈工大操作系统实验4—进程同步

实验步骤

(1)在ubuntu下,用系统提供的sem_open()、sem_close()、sem_wait()和sem_post()等信号量相关的系统调用编写pc.c程序。

(2)在ubuntu上编译并运行pc.c,检查运行结果。


终端也是临界资源

用printf()向终端输出信息是很自然的事情,但当多个进程同时输出时,终端也成为了一个临界资源,需要做好互斥保护,否则输出的信息可能错乱。

另外,printf()之后,信息只是保存在输出缓冲区内,还没有真正送到终端上,这也可能造成输出信息时序不一致。用fflush(stdout)可以确保数据送到终端。

本次实验相较于往届已极大地简化,毕竟时间有限,所以不用在Linux0.11下实现信号量(里面没有sem_open()等系统调用,需要自己添加),仅需要在Ubantu下运行生产者,消费者的相关程序。


首先介绍一下所需函数

int fseek(FILE *stream, long offset, int fromwhere);
函数设置文件指针stream的位置。
如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。
如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置,函数返回一个非0值。

size_t fread(void *buffer,size_t size,size_t count, FILE *stream );   
buffer   是读取的数据存放的内存的指针   
size     是每次读取的字节数   
count    是读取次数   
stream   是要读取的文件的指针 
从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读取到的元素个数,如果不成功或读到文件末尾返回 0。

size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
(1)buffer:是一个指针,对fwrite来说,是要获取数据的地址;
(2)size:要写入内容的单字节数;
(3)count:要进行写入size字节的数据项的个数;
(4)stream:目标文件指针;
(5)返回实际写入的数据项个数count。

 
 算法的思想是:建立一个文件缓冲区,0~9位存储生产出的数据,第10位存储当前读到的位置;因为缓冲区是覆盖写入,例如当消费者消费到第6位,而生产者此时可以生产覆盖前5位,但消费者消费是顺序消费的,必须要读到缓冲区尾才可以再从头读。 
 

这就有必要存储当前读取的位置(因为进程可能被中断,下次再来就不知道读到哪里了),所以下面要执行两次,第一次读出当前所读位置,再根据此位置计算位偏移。

fseek( fp, 10*sizeof(int), SEEK_SET );
fread( &Outpos, sizeof(int), 1, fp);

fseek( fp, Outpos*sizeof(int), SEEK_SET );
fread( &costnum, sizeof(int), 1, fp);

pc.c

#define   __LIBRARY__
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>

#define   Total        500
#define   PNUM         5
#define   BUFFERSIZE   10
/*

*/
int main()
{
    int  i, j, k;
    int  costnum;
    int  Outpos = 0;
    int  Inpos = 0;
    sem_t *empty, *full, *mutex;
    FILE *fp = NULL;

    empty =(sem_t *)sem_open("empty", O_CREAT, 0064, 10);
    full  = (sem_t *)sem_open("full", O_CREAT, 0064, 0);
    mutex = (sem_t *)sem_open("mutex",O_CREAT, 0064, 1);

    fp=fopen("FileBuffer.txt", "wb+");

    fseek( fp, 10*sizeof(int) , SEEK_SET );
    fwrite( &Outpos, sizeof(int), 1, fp);
    fflush(fp);

    if( !fork() )
    {
        for( i = 0 ; i < Total; i++)
        {
            sem_wait(empty);
            sem_wait(mutex);
            
            fseek( fp, Inpos * sizeof(int), SEEK_SET );
            fwrite( &i, sizeof(int), 1, fp );
            fflush(fp);

            Inpos = ( Inpos + 1 ) % BUFFERSIZE;

            sem_post(mutex);
            sem_post(full);
        }
        exit(0);
    }

    for( k = 0; k < PNUM ; k++ )
    {
        if( !fork() )
        {
            for( j = 0; j < Total/PNUM; j++ )
            {
                sem_wait(full);
                sem_wait(mutex);
                /*
                fseek( fp , 10*sizeof(int) , SEEK_SET );
                if(!fread( &outlocate, sizeof(int),1, fp))
                {
                    printf("read error!\n");
                    exit(1);
                }
                */
                /*
                fseek(fp, outlocate*sizeof(int),  SEEK_SET );
                if(!fread( &costnum,  sizeof(int),1,  fp))
                {
                    printf("read error!\n");
                    exit(1);
                }
                */
                fflush(stdout);
                
                fseek( fp, 10*sizeof(int), SEEK_SET );
                fread( &Outpos, sizeof(int), 1, fp);

                fseek( fp, Outpos*sizeof(int), SEEK_SET );
                fread( &costnum, sizeof(int), 1, fp);
                
                printf("%d:   %d\n",getpid(),costnum);

                fflush(stdout);

                Outpos = (Outpos + 1) % BUFFERSIZE;

                fseek( fp, 10*sizeof(int), SEEK_SET );
                fwrite( &Outpos, sizeof(int),1, fp );
                
                fflush(fp);
                sem_post(mutex);
                sem_post(empty);
            }
           exit(0);
        }
    }
    wait(NULL);
    wait(NULL);
    wait(NULL);
    wait(NULL);
    wait(NULL);
    sem_unlink("empty");
    sem_unlink("full");
    sem_unlink("mutex");

    fclose(fp);
    return 0;
}
附report

1.在pc.c中去掉所有与信号量有关的代码,再运行程序,执行效果有变化吗?为什么会这样?
答:在去掉与信号量有关的代码后,执行结果Customer的消费数据没有按递增的顺序输出,且fread()函数将产生错误。
    因为没有信号量P(S)控制,导致生产者可能在缓冲区满后继续生产,导致没有被消费的数据被覆盖,使得消费者消费的数据不是递增序列。
	同时,没有信号量V(S)控制,导致消费者可能在读取所有数据后仍然继续读取,导致读取的数据无效。
	没有mutex信号量控制导致出现多进程并发访问缓冲区,导致出现fread()错误。

2.这样可行吗?如果可行,那么它和标准解法在执行效果上会有什么不同?如果不可行,那么它有什么问题使它不可行?
答:这样不可行。程序在某种情况下会出现死锁状态。
    例如:当mutex = 1,并且生产者要进入生产一个数据,假设此时empty = 0,mutex = 0,P(empty)后小于0,生产者进程进入等待在信号量empty的等待队列上面调用schedule(),
    可是此时并未解锁,即mutex.value值仍然为0。它们都等待在信号量mutex上面。同理,消费者进程也是如此,若mutex.value = 1,full.value = 0,
    在执行完P(mutex)P(full)之后,mutex = 0,并且将消费者进程放入等待在信号量full的等待队列上面,而此时也并未释放mutex,
	因此消费者和生产者进程都等待在mutex信号量上面。进而产生饥饿状态进入死锁。
	


你可能感兴趣的:(操作系统,OS)