您的位置  > 互联网

Linux实验二相比实验的内容与过程阅读代码

实验2比实验1更难。首先,你必须掌握相应的理论知识(读者-写入者问题和消费者-生产者问题),然后才能游刃有余地进行实验。 任务二的代码编写可以参考源码,所以首先要了解源码。

1 实验目的

掌握Linux环境下进程(线程)同步和关键资源的互斥访问方法。

2.实验要求

熟悉Linux操作系统线程创建,利用信号量机制进行同步和互斥访问关键资源。

三、实验内容

本实验包括两个部分:生产者和消费者之间的同步以及读者和作者之间的同步。 请实现以下功能:

有多个生产者线程和多个消费者线程。 我们使用包含 n 个元素的数组来表示 n 个缓冲区。 生产者线程每隔一段时间(例如一秒钟)生产一个产品,并将其放入缓冲区中。 消费者线程等待生产者线程将产品放入缓冲区,然后从缓冲区中取出产品。 请使用信号量机制来实现生产者线程和消费者线程之间的同步。 (生产者-消费者)有一个或多个写入器线程和多个读取器线程。 读取器和写入器之间共享一个缓冲区(缓冲区可以用字符数组表示),写入器在获得对缓冲区的访问权限后将内容写入缓冲区。 读取器在获得对缓冲区的访问权限后从缓冲区中读取内容。 当读取器获得缓冲区访问权限时,其他读取器线程可以访问该缓冲区。 读取器和写入器需要互斥的访问缓冲区。 4.实验内容及流程阅读代码

在实验之前,我们先来理清一下代码。

。C

分析:在.c中,我们根据定义的生产者和消费者的数量生成了相应的空间大小。 接下来,我们定义了一个生产者-消费者管理器,其主要功能是管理关键资源。 (后面会分析)然后就是两个for循环。 一个for循环调用两个生产者执行生产任务,另一个for循环调用消费者执行消费任务。 在这两个循环中,我们都使用了函数。 该函数主要作用是创建一个线程,以第三个参数为入口函数。 传入的四个参数分别是:线程指针、线程属性、线程运行函数起始地址、传递给运行函数的参数。 即生产者的for循环中生成的子线程完成函数中的任务,消费者的for循环中生成的子线程完成函数中的任务。

在.c文件中

分析:函数中,首先获取我们的关键资源,然后随机生成种子数,为我们后续的随机数生成做准备。 然后在while循环中生产产品,直到变成1,我们就不再继续生产产品了。 生产产品主要是调用自定义函数。 在函数中,我们首先给三个信号赋予相应的值,然后随机生成一个随机数,这就是我们的乘积。 然后调用(empty)和(mutex)。 要注意把(empty)放在(mutex)前面,否则会出现死机等现象。 然后将已有的商品数量+1,然后将商品放入缓存区,并输出相应的信息。 最后,我们在线程结束后调用释放信号。

在.c文件中

分析:功能和上面的功能类似,图中都有注释,就不详细介绍了。 然后是功能。 函数中,首先给三个信号赋值相应的值,然后调用(full)和(mutex)并取出一个乘积,然后将乘积数量减1并打印出相应的信息。 最后,我们在线程结束后调用释放信号。

ities.c 文件:

分析:在这个文件中,通过截图中的注释,我们发现这个文件只是定义了一堆初始化和自定义线程创建方法,后面是wait、操作,以便我们在其他地方调用这些方法。 。

ities.h 文件:

分析:该文件中定义了生产者-消费者管理器结构。 其内容主要记录了mutex、full、empty这三个信号,生产者的偏移量,消费者的偏移量,以及已有的产品。 通过数量、缓冲区、缓冲区大小和变量来确定是否结束。 之所以定义这个结构体,是因为这些资源是公共资源,即关键资源,由各个线程共同使用。 然后是线程结构体和一些方法的声明。

至此,我们代码的关键部分基本已经分析完毕,对实验的原理也有了一定的了解。 接下来我们就可以开始实验了。

任务1.编写生产者程序和消费者程序对应的文件

分析:根据我们已有的文件生成对应的.o文件,也就是第三行的文件名。 (注意第二行变量中添加了-)

运行结果:

正如您所看到的,我们的生产者-消费者程序已准备好运行,并且可以通过输入 q 来停止。 (因为它的定义是如果我们输入q,它就会变成1)

至此,我们的任务一就完成了,接下来就是任务二了。

任务2:读写器程序.c

分析:定义2个和6个(数量自己定义)后,给对应的线程分配足够的空间,并生成一个读写器管理器来管理关键资源。 接下来是编写器线程和读取器线程的初始化,这与生产者和消费者代码类似。 主要是看下面这部分代码。

sh 文件:

分析:该头文件主要定义了读写器程序管理器的一些方法和结构体。 首先是读写器管理器的结构。 该结构体中包含信号量mutex和(互斥信号用于实现对缓冲区的互斥访问,信号量主要用于实现对变量的互斥访问),变量用于存储读取器的数量,使用数组存储字符,类似于作者的工作。 它还用于记录缓冲区大小以确定线程的状态。 接下来是线程结构体和一些方法的定义。 由于这些内容与生产者和消费者的代码部分类似,因此不再详细描述。

sc 文件:

分析:可以看到这个文件中主要定义了一堆初始化和自定义线程创建方法,后面是wait、操作,和任务一代码类似,截图里有注释,所以我赢了不谈细节。 接下来是关键的读取器部分和写入器部分代码。

.c 文件:

分析:首先看这个文件中定义的方法。 该方法主要是先获取mutex 、和sum的值。 然后定义buf数组来存放工作的内容。 接下来,首先使用wait语句来确保线程可以互斥地访问变量。 如果值为0,则表示当前没有读者正在阅读该作品。 对关键资源会做互斥访问,保证读者和作者不会同时对关键资源进行操作(类似于第一个读到作品的人先得到它)。 ),然后数量加1并释放信号。 当读者读完后,我们首先进行互斥访问,并先减1。 如果该值为0,则说明我们现在是最后一个读者,需要释放互斥信号(类似于最后一个读完的人返回工作),然后再释放信号。 文件中定义的另一个方法是实现线程的运行。 如果为0,则始终执行读操作。

.c 文件:

分析:首先看这个文件中定义的方法。 方法中,首先获取互斥信号的值,然后调用wait方法,保证对关键资源的互斥访问。 然后,在循环中向缓冲区写入字符,首先随机获取一个偏移量(0到26),然后将这个偏移量与字符'a'的ASCII码值相加,得到'a'到'z'一个字符。 当我们填充完缓冲区后,就实现了的写入任务,然后输出相应的信息并释放互斥信号。 另一种方法是实现写入线程的操作,当值为0时,将一直执行写入操作。

文档:

分析:生成的文件有so、.o、.o、.o。 只需填写第三行即可,与任务1的操作类似。

运行结果:

正如您所看到的,我们的读写器程序已准备好运行,并且可以通过输入 q 来停止。 (因为它的定义是如果我们输入q,它就会变成1)。 通过观察,我们发现读者读到的内容始终与最新作者写的内容相同,说明代码没有问题。

至此,我们的任务二就已经完成了。

需要注意的是,.h和h这两个头文件我没有截图。 你可以自己写。 这很简单。 只需复制任务一的代码即可。

至此,我们的实验就完成了! 如果你有什么想法,可以在评论区提出,一起分享。