北邮操作系统消费者与生产者实验报告x
时间:2020-10-04 16:55:00 来源:勤学考试网 本文已影响 人
操作系统实验课程报告
课题:消费者与生产者实验
姓 名 张涛
学 院 计算机学院
班 级 2011211311
学 号 2011211419
2013年 12月14日
(
( 2) 阅读 Linux 的 pthread.h 源码文件,分析线程的创建过程。
(
( 2) 阅读 Linux 的 pthread.h 源码文件,分析线程的创建过程。
1.实验目的:
1) 理解线程同步的思想和方法,学会用线程同步解决临界区问题,本次实验解决生产 者消费者问题
2 了解 windows 系统或 linux 系统下中信号量的使用方法。
2.实验预备内容
(1) 阅读Linux的sched.h源码文件,加深对进程管理概念的理解。
这个文件长达 2616 行,这里截取第 1221~1548 行抄录在实验报告最后,即结构体 task_struct,地位相当于 PCB。
下面对几个比较重要的参数,结合本人的了解 以及 网上查阅的资料 做一点解释。
中括号内的数字为代码行号,下同。
volatile long state:【1222】进程状态字,表示进程当前的状态(运行、就绪、等待、僵
死、暂停、交换) ,分别对应已定义好的常量;
TASK_RUNING :正在运行或可运行状态;
TASK_INTERRUPTIBLE :可打断睡眠状态; TASK_UNINTERRUPTIBLE :不可打断睡眠状态;
TASK_ZOMBLE :僵死状态;
TASK_STOPPED :暂停状态;
交换状态。
void *stack:【1223】进程所使用的栈空间;
unsigned int flags:【 1225】进程标志(创建、关闭、跟踪、被跟踪、内核 dump 等),同
样对应已定义好的常量;
unsigned int rt_priority :【 1237】表示本进程的实时优先级;
const struct sched_class *sched_clas、sstruct sched_entity se:【 1239,1240】分别是调度类和 调度实体,这两个结构包含了用于任务调度的完整的信息(进程信息、调度策略等) ;
unsigned int po l i cy : 【 1 260】进程的调度策略标志,有三种调度标志:
SCHED_OTHER :普通进程的调度策略,基于优先权的轮转法; SCHED_FIFO :实时进程的调度策略,基于先进先出的算法;
SCHED_RR :实时进程的调度策略,基于优先权的轮询法。
struct list_head tasks:【 1274】任务队列,为一双向循环链表;
int pdeath_signal:【1282】父进程终止时产生的信号;
pid_t pid :【1294】进程标识符,操作系统每创建一个新的进程就要为这个新进程分配一 个进程控制块(PCB),系统内核通过pid区分这些进程的;
struct task_struct *real_parent:【1307】本进程的父进程的 PCB;
struct list_head children:【1312】本进程的子进程列表;
struct list_head ptraced: 【1321】本进程正在使用 ptrace 监视的进程列表;
struct thread_struct thread:【1375】本进程下属的线程集;
struct signal_struct *signal、 struct sighand_struct *sighand:【 1383,1384】分别是进程运行时 产生的信号以及信号处理模块。
pthread 接口说明
#include <pthread.h>
1、创建
int pthread_create( pthread_t *tid, const pthread_attr_t *attr, void *(* func) (void *), void *arg ); attr: 线程属性包括:优先级、初始栈大小,是否应该成为一个守护线程。
缺省设置, NULL
后面是线程要执行的函数和参数
成功返回 0
2、等待一个给定线程终止
int pthread_join( pthread_t tid, void **status);等待线程结束 critiction 可以在进程中使用, mutex 只可在进程中使用
statues返回等待线程的返回值
multiple defi niti on of ' __dso_ha ndle'
/usr/lib/gcc/i486-linux-gnu/4.4.3/crtbegin.o:(.data+0x0): first defined here
threadTest: In function '_init':
(.init+0x0): multiple definition of '_init'
/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crti.o:(.init+0x0): first defined here
/tmp/cchm2SmY.o:(.data+0x0): multiple definition of 'flag' threadTest:(.data+0x8): first defined here /tmp/cchm2SmY.o: In function 'threadMe
3、 得到自身的 pid
pthread_t pthread_self(void);
4、 pthread_detach 函数
int pthread_detach( pthread_t pid );
把指定的线程转变为脱离状态
一个线程或者是可汇合的(joinable,缺省值),或者是脱离的(detached)。当一个可汇 合的线程终止时,它的线程ID和退出状态将留到另一个线程对它调用 pthread_join。脱离线
程却象守护进程:当它们终止的时, 所有相关资源都被释放, 我们不能等待它们终止。
如果 一个线程需要知道另一个线程什么时候终止,那就最好好吃第二个线程的可汇合状态。
本函数通常由想让自己脱离的线程调用,如下语句
pthread_detach( pthread_self() );
5、 终止一个线程
void pthread_exit( void *statue );
指针sttus不能指向局部于调用对象,因为线程终止时这样的对象也消失
( 3) 阅读 Linux 的 semaphore.h 源码文件,分析线程的创建过程。
typedef struct sem_t_ * sem_t;
PTW32_DLLPORT int __cdecl sem_init (sem_t * sem,
int pshared, unsigned int value);
PTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_wait (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_post (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem,
int coun t);
PTW32_DLLPORT int __cdecl sem_close (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_u nlin k (co nst char * name);
int sem_init(sem_t *sem,int pshared,unsigned int value。用于信号量的初始化,第一个参数 为信号量的名称,第二个参数为信号量的共享属性,一般设为 0 (不共享),第三个参数为
信号量的初始值。
int sem_wait(sem_t *sem)。相当于上面的 wait()操作,作用是当信号量的值大于 0时,给
信号量的值减1,否则会阻塞直至信号量的值大于 0。它是一个原子操作。
int sem_post(sem_t *sem)。相当于上面的 signal(操作,作用是给信号量的值加 1。它也是
一个原子操作。
******************************************************************************/
实验环境
此实验采用的是 Win8下Microsoft Visual Stdio 2012 工程。由于系统不包含 semaphored,
sched.h及pthread.h头文件,所以这些头文件都是从网上 FTP资源下载后,加入工程的。
程序中加入 #pragma comment(lib , "pthreadVC2.lib")来调用 pthread链接库。
文件根目录下已下载 pthreadVC2.dll ******************************************************************************/
实验步骤
A .设计思路
这个问题是进程同步的经典问题之一,基本思路是设置三个信号量: mutex信号量,用
于控制生产者和消费者对于缓冲区的互斥访问; empty信号量,记录当前为空的缓冲区的数
目,初始化为所有缓冲区的数目 BUF_SIZE full信号量,记录当前已满的缓冲区的数目, 初始化为0。
******************************************************************************/
一个buffer,一个生产者,一个消费者
(1)规则
只有buffer为空才能put;只有buffer中有数据才能get;不允许多个put操作同时进行; 不允许多个
get操作同时进行。
这时buffer变成了临界资源,消费者之间需要互斥地使用,生产者之间也需要互斥地使 用,消费者和生产者之间也需要互斥地使用,这时设置两个信号量
s1, s2实现同步,例外设置 S信号,代表buffer这种临界资源,用于互斥,称之为互
L /宀口曰. 斥信号量。
(2 )实现流程
<生产者>p (s1)"判断buffer是否空"
p ( s)
"是否可进行put,是否有其他进程占用
buffer"
putv (s)"释放buffer,让其他进程可以使用 "v (s2)
"给消费者进程释放一个 buffer中有数据的
信号”
<消费者>p( s2)"判断是否有数据”
p( s)"是否可进行get,是否有其他进程占用”
getv ( s)"释放buffer,让其他进程可以使用 "v ( s1)
"给生产者释放一个 buffer为空的信号”通过3个信号量,实现了消费者和生产者之间同
步关系,也保证了在某个时刻只有一个进程使用临界资源 buffer。
生产者进程的代码结构描述如下:
while(true)
{ n extp=produce();wait(empty);wait(mutex);put (n extp);sig nal(mutex);sig nal(full); }
消费者进程的代码结构描述如下:
while(true)
{ wait(full);wait(mutex); nextc=get();sig nal(mutex);sig nal(empty);c on sume( nextc); }
这里两个wait语句的次序并不能调换, 这是因为如果将两个 wait操作即wait( full)和
wait ( mutex )互换位置,或者将 release( mutex )与release( full )互换位置,当缓冲区存
满产品时,生产者又生产了一件产品,它欲向缓冲区存放时将在 empty上等待,但它已经
占有了使用缓冲区的权利。这时消费者要取产品时将停留在 mutex上得不到使用缓冲区的
权利,导致生产者等待消费者取走产品,而消费者却在等待生产者释放使用缓冲区的权利, 这种相互等待永远结束不了。因此进程将会发生死锁。
******************************************************************************I
B .实验代码分析
/*
Producer
Produce items and try to put it into the buffer
If the buffer is full, wait ing
When produced, we output like that: Producer 2 produced 222
When successfully put, we output: Producer 2 have put product 222 into the buffer after 100 millisec onds
The milliseconds is the timespan from the producer produced the item
When the buffer is full but the producer put the item, an error occurred
(No way it will happe n...)
*/
#define PTIMESPANMA)5000
void* producer( void* params)
{
int id=*(i nt *)params; while (true)
{
SLEEga nd()%PTIMESPANMAX
buffer item rnd=rand();
long beg in, end;
CLOCKbegi n);
printf("%ld:\t Producer %d\t produced %d\n" , (long)(begin-threadStart), id, rnd);
sem_wait(&empty);
pthread_mutex_lock(&mutex);
(CLOCKS nd), i nsert item(r nd))
? printf("%ld:\t Producer %d\t have put %d\t after %d\t milliseconds\n" ,end-threadStart, id, rnd, en d-begi n)
:printf( "%ld:\t Producer %d\t Report Error!\n" , end-threadStart, id);
pthread_mutex_u nlock(&mutex);
sem_post (&full);
}
}
******************************************************************************/
消费者:
/*
Con sumer
Try to con sume items from the buffer
If the buffer is empty, wait ing
When try to con sume, we output like that: Con sumer 3 want to con sume
When successfully con sumed, we output: Con sumer 3 con sumed 222 after 100 millisec onds
The milliseconds is the timespan from the consumer try to consume the item
When the buffer is empty but the con sumer con sumed the item, an error occurred
(No way it will happe n...)
*/
#define CTIMESPANMAX 5000
void * consumer( void * params)
{
int id=*( int *) params ;
while (true)
{
SLEEP(rand()% CTIMESPANMAX);
long begi n, end;
CLOCK(begi n);
pri ntf( "%ld:\t Con sumer %d\t want to con sume\n" , beg in-threadStart, id);
sem wait(&full);
pthread_mutex_lock(&mutex);
buffer_item item=buffer[currentlndex];
(CLOCK(e nd), remove item(&item))
? printf( "%ld:\t Consumer %d\t consumed %d\t after %d\t milliseconds\n"
SLEEP(1);
SLEEP(1);
en d-threadStart, id, item, en d-beg in)
en d-threadStart, id, item, en d-beg in)
:printf( "%ld:\t Consumer %d\t Report Error!\n" , end-threadStart, id); pthread_mutex_u nlock(&mutex);
sem_post(&empty);
}
}
******************************************************************************/
主函数:
int main(int argc, char* argv[])
{
sra nd(u nsig ned in t(time(0)));
//In it mutex, full and empty
pthread_mutex_init(&mutex, NULL );
sem init(&full, 0, BUFFER SIZE); sem」n it(&empty, 0, BUFFER_SIZE);
int i=0;
// e... No item in the buffer now, so full is "full"
for(i=0; i< BUFFER_SIZE; i++)
sem wait(&full);
int sleepTime=O;
int producerCou nt=O;
int con sumerCou nt=O
pri ntf( "How long to sleep before term in ati ng:");
scanf s('%d", &sleepTime);
printf( "The number of producer threads:");
scanf s('%d", &producerCount);
printf( "The number of consumer threads:');
sca nf s('%d", &con sumerCou nt);
//In it threadStart
CLOCK (threadStart);
// Create producer threads
for(i=0; ivproducerCou nt; i++)
{
pthread t pid;
pthread create(&pid, NULL , producer, (void *)&i);
SLEEP(1);
}
// Create con sumer threads
for(i=0; i<c on sumerCou nt; i++)
pthread t pid;
pthread_create(&pid, NULL , consumer, (void *)&i);
}
//Just wait and the n... end
SLEEP(sleepTime);
printf( "End of time\n“);
return 0;
}
****************************************************************************
C.实验运行结果:
1?程序初始化格式:
DMJsers\tao\documents\visual studio 2012\Proj ec ts\Co n so le A ppi i c ati on, ?
Hlow long to sleep before termiiiatingf: 50000
The nunbei^ of producer threads: 3
The numbejr of consuner threads - 3_
****************************************************************************
2?程序运行结果:
r \ .、八.~ *
生产者:
producer 0 1 2
消费者:
con sumer 0 1 2
生产者生产后将数据put到buffer中去
消费者在申请 mutex (want to consume)后,即可消费。
42:
PFoduceF*
0
produced
1846?
42 :
Producer
0
have put
18467
aft Ei'
G
nilllseconds
44:
Producei*
1
produced
18467
44;
Producer
1
have put
1R467
after
0
milliseconds
16:
Producei*
2
produced
18467
4G:
Producei*
2
haue put
18467
a( ter
0
milliseccnds
48:
Consumei*
0
want to ii
48:
Consumer
consuned
18467
af tei'
0
millisecionds
&0:
Consunei'
1
want to i
consume
50:
Cans unet'
1
consuned
1846?
af tei*
B
milllsecionds
52 :
Cansunei'
2
Nairit to 耳
consume
52:
2
consumed
16467
after
0
rtilliseconds
1377;
Producer
0
produced
265Q0
137?:
Producei*
0
have put
265U
after
e
nilliseconds
1379:
Producei*
1
produced
265QS
1379 :
Producer*
1
haue put
af ter
0
milliseconds
1381:
Producer
2
produced
265W
1381:
Produtcei?
2
have put
2£50S
after1
s
nilllseconds
119S7
Con^uner
1
uant to consume
11960
Consumer
1
consuned 28145
after
1
billiseconds
12 977
Producer
Q
produced 16827
12977
Producer
0
have put 16827
after
6
nilliseconds
12985
Producei*
1
produced 16827
12985
Fs*0>ducEl*
1
have put 16827
after
e
nilliseconds
13 423
Cun^uiner
0
vant to consume
13423
Consuner
Q
consuned 1682?
after
0
billiseconds
13438
Consunei*
2
uant to consume
13438
Con^uner
2
consumed 1G827
Af ter
0
milliseconds
1343?
Consumer
1
vant to con Slime
14SH1
Producer
2
produced 16827
14S01
Producer
2
have put 16827
after
P
nilliseconds
14502
Consumer
1
consumed 1682?
after
1863
nilllseGonds
177E2
Consumer
0
want to consume
17797
Consumer
2
vant to consume
179-10
Producer
0
produced 491
1794R
Producer
0
have put 491
after
0
billiseconds
17940
Con^uner
0
consumed 491
af ter
15B
inillisecond^
17948
Produce!'
1
produced 491
17948
Producer
1
have put 491
after
Q
ntilliseconds:
17948
Consumer
2
consumed 491
after
1S1
milliseconds
1^862
Consumer
1
uant to consume
D:\Use rs\ta o\d oc u m e nts\v i su a I studio 20l2\Projects\ConsoleApplication..
□
X
由结果看出,三个消费者和三个生产者一直处于相互互斥工作状态, 并未出现死锁,拥堵等
异常状态。程序运行成功。
实验总结