Linux进程同步设计报告Word文件下载.docx
《Linux进程同步设计报告Word文件下载.docx》由会员分享,可在线阅读,更多相关《Linux进程同步设计报告Word文件下载.docx(9页珍藏版)》请在冰点文库上搜索。
编写读者—写者同步程序,要求:
(1)在Linux下用C语言编程,利用信号量实现进程同步;
(2)读写要求随机产生,如按键盘输入R或W,动态显示当前状态,有多少个读
者、写者在等待访问;
每个读者写者的时间可预先设定;
(3)读者写者共享一个数据区,用数组表示,如可初始化为1、2、3、4、5、6、
7、8、9、10、;
写者随即对其修改。
Linux环境下相关技术:
线程定义:
pthread.h
信号量:
sem.h
读写锁与互斥量相似,不过读写锁允许更高的并行性。
互斥量要么是锁住状态,要不是不加锁状态,而且一次只有一个线程可以对其进行加锁。
读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。
这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。
写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。
读写锁有三种状态:
读模式加锁,写模式加锁,不加锁。
1.当读写锁是写加锁时,在这个锁被解索之前所有企图对它加锁的线程都将要阻塞。
2.当读写锁是读加锁时,在这个锁被解索之前所有企图以读模式对它加锁的线程都可以获得访问权;
以写模式加锁的线程将堵塞,并且堵塞随后的读模式加锁。
这样可以避免读模式锁长期占用,导致等待的写模式锁请求一直得不到满足。
信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。
所测试的线程可以锁定而使用它。
若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。
然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。
只有在资源被释放时,进程才再次变为可运行。
只有可以睡眠的函数才能获取内核信号量;
中断处理程序和可延迟函数都不能使用内核信号量。
内核信号量是structsemaphore类型的对象,它在<
asm/semaphore.h>
中定义:
structsemaphore{
atomic_tcount;
intsleepers;
wait_queue_head_twait;
}
count:
相当于信号量的值,大于0,资源空闲;
等于0,资源忙,但没有进程等待这
个保护的资源;
小于0,资源不可用,并至少有一个进程等待资源。
wait:
存放等待队列链表的地址,当前等待资源的所有睡眠进程都会放在这个链表中。
sleepers:
存放一个标志,表示是否有一些进程在信号量上睡眠。
通过对读写锁以及信号量的理解,以及对题目的分析,我们需要用信号量来实现进程间的同步,通过读写锁来访问临界资源。
2数据结构
读者——写者问题中涉及的数据结构包括读者、写者共用的缓冲区,读写锁,以及同步需要的信号量集。
主要应用头文件以及主要变量定义和初始化如下:
#include<
semaphore.h>
pthread.h>
sem_tmy_sem;
inta[10]={1,2,3,4,5,6,7,8,9,10};
pthread_rwlock_trwlock;
sem_init(&
my_sem,0,3);
pthread_rwlock_init(&
rwlock,NULL);
读线程的工作如下:
void*pthread_read(void*arg)
{
intid;
while(flag){
if(sem_wait(&
my_sem)==0)
{
pthread_rwlock_rdlock(&
rwlock);
//printf("
I'
m%dread\n"
(int)arg);
srand(time(0));
id=rand()%10;
printf("
m%dreadthread!
I'
mreadingthe%dshu:
%d\n"
(int)arg,id,a[id]);
sleep(3);
if(flag2){
count_read++;
}
pthread_rwlock_unlock(&
sem_post(&
my_sem);
}
sleep
(2);
printf("
mthe%dread_thread,exit!
!
\n"
pthread_exit((void*)1);
写线程的工作如下:
void*pthread_write(void*arg)
intnum;
pthread_rwlock_wrlock(&
num=rand()%20;
a[id]=num;
m%dwritethread!
mwritingthe%dshu:
%d\n"
(int)arg,id,num);
count_write++;
sleep
(1);
mthe%dwrite_thread.exit!
3流程图
读、写线程流程图如下图所示。
4实现技术
为了实现上述设计,使用Linux系统环境,采用C语言。
具体采用的技术如下:
(1)通过Linux内核信号量机制实现进程同步。
(2)通过读写锁来访问临界资源。
(3)采用模拟随机数的方法随机访问缓冲区内的资源。
实现步骤如下:
(1)定义信号量集,定义读写锁,并将其初始化。
(2)根据输入的读写线程的数量分别创建线程。
(3)在读写线程中分别构造相应代码,通过wait信号量实现进程同步。
(4)对临界区加上读写锁,防止数据出现紊乱。
运行结果如下:
5设计结论和心得
通过课程设计得到如下结论:
(1)如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里,直到没有任何写者或读者。
如果读写锁没有写者,那么读者可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该读写锁。
一次只有一个线程可以占有写模式的读写锁,但是可以有多个线程同时占有读模式的读写锁.正是因为这个特性,当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞.当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是如果线程希望以写模式对此锁进行加锁,它必须直到所有的线程释放锁.通常,当读写锁处于读模式锁住状态时,如果有另外线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求长期阻塞.读写锁适合于对数据结构的读次数比写次数多得多的情况.因为,读模式锁定时可以共享,以写模式锁住时意味着独占。
(2)信号量可以分为二进制信号量(binarysemaphore)、整型信号量(integersemaphore)和记录型信号量(recordsemaphore)。
信号量通过一个计数器控制对共享资源的访问,信号量的值是一个非负整数,所有通过它的线程都会将该整数减一。
如果计数器大于0,则访问被允许,计数器减1;
如果为0,则访问被禁止,所有试图通过它的线程都将处于等待状态。
计数器计算的结果是允许访问共享资源的通行证。
因此,为了访问共享资源,线程必须从信号量得到通行证,如果该信号量的计数大于0,则此线程获得一个通行证,这将导致信号量的计数递减,否则,此线程将阻塞直到获得一个通行证为止。
当此线程不再需要访问共享资源时,它释放该通行证,这导致信号量的计数递增,如果另一个线程等待通行证,则那个线程将在那时获得通行证。
有如下几点心得体会:
(1)读写锁比互斥量允许更高的并行性。
当读写锁是写加锁时,在这个锁被解索之前所有企图对它加锁的线程都将要阻塞;
当读写锁是读加锁时,在这个锁被解索之前所有企图以读模式对它加锁的线程都可以获得访问权;
(2)慎用读写锁。
之所以要“慎用”,是因为能够利用读写锁真正达到想要的效果的情景不多,如果情景使用不正确,使用的效率还不如一般的锁。
而且使用读写锁有一个非常容易的错误,在读锁递归使用时(重入时)如果有写锁lock,很多的实现版本上会发生死锁。