“计算机操作系统”课程设计大作业一、实验题目:linux下利用互斥实现线程访问共享资源二、实验目的:掌握线程创建和终止,加深对线程和进程概念的理解,会用同步与互斥方法实现线程之间的通信。三、实验内容事先了解操作系统中经典的生产者-消费者问题,安装linux虚拟机(可以用virtualbox或者vmware软件)。在linux下创建三个生产者线程(P1,P2,P3)和一个消费者线程(C1),生产者和消费者线程共享一个长度为2KB的环型公共缓冲区,生产者向缓冲区写入消息,消费者从缓冲区中取走消息显示到屏幕。只要缓冲区未满,生产者可将消息送入缓冲区;只要缓冲区未空,消费者可从缓冲区取走一个消息。每个消息具下列结构格式:消息长度(1个字节),消息内容(n个字节)。每个生产者每隔2秒生产一个消息加入缓冲区,并把消息产生时间和内容记录在一个文本文件中。P1每次生产的数据为26个大写字母,P2每次生产的数据为26个小写字母,P3每次生产的数据为10个阿拉伯数字。消费者C1每隔3秒读取缓冲区中的一个消息并将消息内容显示到屏幕上。用两种方法(不采线程用互斥和采用线程互斥技术)编写上述功能的程序,对比两种程序运行结果有何区别?不采线程用互斥时存在什么问题?备注:编程中用到的函数创建线程函数:pthread_create互斥锁初始化:pthread_mutex_init互斥锁加锁:pthread_mutex_lock互斥锁解锁:pthread_mutex_unlock四、实验要求:每人完成一份大作业实验报告。报告分设计思想、数据定义、处理流程、源程序、运行结果截图、设计体会等部分。1)给出数据定义和详细说明;2)给出实现思想和设计流程;3)调试完成源程序;4)屏幕观察运行结果;5)总结自己的设计体会;编程语言及操作系统平台不限。五、提交内容本大作业每个人必须单独完成。最后需提交的内容包括:源程序(关键代码需要注释说明)、可运行程序、算法思路及流程图、心得体会。大作业必须以WORD附件的形式提交。大作业严禁抄袭。发现抄袭一律以不及格论。大作业内容要完整,一定要有算法思路、流程图、心得体会、运行输出信息截屏等内容,如果只提交源代码则大作业成绩记为不合格。2016-10-20linux下利用互斥实现线程访问共享资源一、原理的理解生产者-消费者问题是一个经典的线程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。在同一个线程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。多个生产/消费者在有界缓冲上操作。它利用N个字节的共享内存作为有界循环缓冲区,利用写一字符模拟放一个产品,利用读一字符模拟消费一个产品。当缓冲区空时消费者应阻塞睡眠,而当缓冲区满时生产者应当阻塞睡眠。一旦缓冲区中有空单元,生产者线程就向空单元中入写字符,并报告写的内容和位置。一旦缓冲区中有未读过的字符,消费者线程就从该单元中读出字符,并报告读取位置。生产者不能向同一单元中连续写两次以上相同的字符,消费者也不能从同一单元中连续读两次以上相同的字符。二、完成步骤1、思路分析本作业是完善课件上的线程综合实例的练习生产者-消费者问题,重构这个程序的框架,完成性能分析,使之进一步理解掌握Linux下线程的同步、通信以及互斥和多线程的安全问题。一般情况下,解决互斥方法常用信号量和互斥锁,即semaphore和mutex,而解决这个问题,多采用一个类似资源槽的结构,每个槽位标示了指向资源的指针以及该槽位的状态,生产者和消费者互斥查询资源槽,判断是否有产品或者有空位可以生产,然后根据指针进行相应的操作。同时,为了告诉生产者或者消费者资源槽的情况,还要有一个消息传送机制,无论是管道还是线程通信。然而,本次试验有几个特殊的要求:A、循环缓冲。B、除了stderr,stdout等外,只用小于2个的互斥锁、C、放弃资源槽分配机制,采用额外的数据结构。D、生产者一直持续生产,形成生产消费的良性循环。首先,使用一个互斥锁,意味着资源槽机制就不能使用了。因为资源槽虽以用一个互斥锁完成,但是需要有额外的通信,如果使用管道通信,则管道也必须是互斥,这就不满足1个互斥锁的要求。其次,要求生产者一直生产,这就否定了另外一种方法:消费者、生产者的位置均平等,消费者消费的时候生产者不能生产,生产者生产的时候消费者不能消费。因此,就需要采用A要求,也就是循环链表的形式。为了保证互斥要求,需要定义一个数据结构,这个数据结构包含两个指针,一个读一个写,同时有一个资源数目量,告诉生产者和消费者是否可以生产或者消费。由于该数据结构很小,因而可以对此结构互斥访问。同时,对于每组数据,都有一个标志位,表示此组数据是否被占用,生产者和消费者均可以先占用此位置然后完成相应的操作。当消费者互斥访问此结构时,首先判断是否有数据可以取,如果没有,直接等待,若有数据可取,先更改标志位占用此数据,并将资源数目-1。然后交出互斥,把数据拷贝到自己缓冲区内,清空数据。当生产者访问时,首先判断有没有空位可以生产,如果没有,直接等待,若有数据可以生产,先判断该位是否被占用,如果没被占用,则占用此位置进行生产。生产完成后,将占用位改为未占用,同时将资源数目+1。这个过程可以如图所示:读指针写指针资源数目数据块占用位数据块占用位数据块占用位数据块占用位数据块数据块占用位数据块占用位数据块占用位占用位2、重要函数和数据结构(1)数据结构#defineN5//生产者的数目#defineN215//消费者数目#defineM20//缓冲数目#definedebug0//调试模式#definenowait1//是否添加额外等待pthread_mutex_tmutex;//互斥信号量intprod_id=0;//生产者idintproc_id=0;//消费者id//成功以及失败次数计数intSucc[N+N2];intFail[N+N2];//用于生产的填充数据chardt[30]=aaaaaaaaaabbbbbbbbbbcccccccccc;inttoexit=0;//退出标记//共用数据区structdata_struc{intOccupied;//该位置是否被占用无论生产还是消费chard[1024];//数据structdata_struc*nx;//下一个指针};//互斥量structmtx{structdata_struc*rd_p;structdata_struc*wr_p;intcnt;//产品数量};structmtx*mymutex;(2)函数说明voidprintinfo();//调试函数,打印公用数据区的内容和状态voidCreate_Empty_DS();//初始化循环链表void*buy();//消费者void*sell();//生产者void*Recount();//数据统计,同时控制程序自动退出3、程序流程图A、主函数Main函数开始初始化公共数据区初始化信号量创建生产者进程成功?创建消费者进程成功?创建统计进程成功定时到退出YYYNNNB、统计线程进程开始定时到?时间+1秒清屏打印消费生产情况进程结束修改退出标记YNC、生产者线程进程开始要求加锁加锁成功?检查资源数目是否有空位设置取得成功设置取得失败失败计数器+1解锁取得成功?生产数据要求加锁加锁成功?增加资源数目解锁要求退出?进程结束YNYYYYNNNND、消费者线程进程开始要求加锁加锁成功?检查资源数目有产品?设置取得成功设置取得失败失败计数器+1解锁取得成功?消费数据要求加锁加锁成功?解锁要求退出?进程结束YNYYYYNNNN减少资源数目三、实验数据1、程序运行截图虚拟机:VMwareWorkstation7.0,win7;硬件:i3,2GDDR1333;编译:gccproduct.c–pg–lpthread;报告生成:gprof–b。运行时截图:CPU占用情况(无额外等待):2、原始数据由于程序要求使用gprof进行分析,而在机器配置较好的情况下,使用gprof可能分析不出每个函数执行时间,甚至连函数都分析不全,因而在实际代码中添加了一些空循环来增加cpu占用。为了更便于观察,首先假设如下场景:消费者、生产者在执行各自动作前,均usleep(1),同时,消费者在成功消费之后,也会usleep(3)来为其他消费者让位。同时,也将测试没有任何等待的数据。程序内有一计时线程,每个一秒会显示一次生产消费情况,当60秒后程序自动退出,从而保存数据已被分析。生产者数量1消费者数量1缓冲长度20额外等待有CPU占用0.3%函数分析情况无法分析到消费者、生产者线程程序运行情况生产者:#1成功次数:12243失败次数:12213消费者:#1成功次数:12223失败次数:0生产者数量1消费者数量1缓冲长度20额外等待无CPU占用97%函数分析情况%cumulativeselfselftotaltimesecondssecondscallsTs/callTs/callname54.592.082.08buy45.413.811.73sell0.003.810.0010.000.00Create_Empty_DSCallgraphgranularity:eachsamplehitcovers4byte(s)for0.26%of3.81secondsindex%timeselfchildrencallednamespontaneous[1]54.62.080.00buy[1]-----------------------------------------------spontaneous[2]45.41.730.00sell[2]-----------------------------------------------0.000.001/1main[8][3]0.00.000.001Create_Empty_DS[3]-----------------------------------------------Indexbyfunctionname[3]Create_Empty_DS[1]buy[2]sell程序运行情况生产者:#1成功次数:21260失败次数:1146117485消费者:#1成功次数:21240失败次数:1151177763生产者数量1消费者数量5缓冲长度20额外等待有CPU占用0.3%函数分析情况无法分析到消费者、生产者线程程序运行情况生产者:#1成功次数:24829失败次数:0消费者:#1成功次数:4900失败次数:15102消费者:#2成功次数:5261失败次数:14386…..消费者:#5成功次数:5069失败次数:14752生产者数量1消费者数量5缓冲长度20额外等待无CPU占用97%函数分析情况%cumulativeselfselftotaltimesecondssecondscallsTs/callTs/callname85.292.902.90buy14.713.400.50sell0.003.400.0010.000.00Create_Empty_DSCallgraphgranularity:eachsamplehitcovers4byte(s)for0.29%of3.40secondsindex%timeselfchildrencallednamespontaneous[1]85.32.900.00buy[1]-----------------------------------------------spontaneous[2]14.70.500.00sell[2]--------------------------------