实验一 体验 Nachos 下的并发程序设计
一、小组成员及分工
a:main.cc 的修改、threadtest.cc 的修改和实验报告
b:dllist.cc 的实现
c:dllist-driver.cc 的实现和实验报告的完成
d:dllist.h 的实现和 Makefile.common 的修改
二、实验目的
对 nachos 进行熟悉,并初步体验 nachos 下的并发程序设计。
三、实验内容
1.安装 nachos;
2.用 C++实现双向有序链表;
3.在 nachos 系统中使用你所写的链表程序并演示一些并发错误
四、实验步骤
1.首先明确 Nachos 各部分的关系
在~/nachos/nachos-3.4/code/下有一个 Makefile.common,在 code/的各个子目录下的
Makefile 都继承这个 Makefile.common。通过阅读 main.cc 知道,main 函数一旦启动,
立即调用 Initialize,进行初始化的操作,然后对相应的参数进行处理,之后在分模块进
行相应模块下的函数调用,执行相应的功能。
2.编写相应的函数
实验要求利用对双向链表的操作来演示并发程序可能出现的错误,首先需要实现双
向链表 dllist,包括 dllist.h,dllist.cc。当 DLList 类实现后,需要编写链表驱动函数 Insert
和 Remove 来对链表进行驱动。通过改写 threadtest.cc,使得多个线程在没有进行任何
互斥操作的情况下对同一数据结构进行操作,在这个过程中就可能出现并发错误。改写
Makefile.common 和 main.cc。
3.详细设计
a)dllist.h(~/nachos/nachos-3.4/code/threads/)
类 DLList 的声明
class
DLLElement {
public:
DLLElement(void *itemPtr,int sortKey);//initialize a list element
DLLElement *next;//next element on list
DLLElement *prev;//previous element on list
int key;
void *item;
};
class
DLList {
public:
DLList();//initialize the list
DLList(int type);
~DLList();//de-allocate the list
void Prepend(void *item);//add to head of list
void Append(void *item);//add to tail of list
void *Remove(int *keyPtr);//remove frome head of list
bool IsEmpty();//return true if list has elements
void SortedInsert(void *item,int sortKey);
void *SortedRemove(int sortKey);//remove first item with key==sortKey
private:
DLLElement *first;
DLLElement *last;
int yield_type;//different yield positon
};
b) dllist.cc(~/nachos/nachos-3.4/code/threads/)
类 DLList 方法的实现,其中核心操作 Remove,SortedInsert。
//----------------------------------------------------------------------
// DLList::Remove
Remove the first "item" from the front of the dllist.
//
//
// Returns:
//
Pointer to removed item, NULL if nothing on the list.
//----------------------------------------------------------------------
void *
DLList::Remove(int *keyPtr)
{
DLLElement *temp;
void *tempitem = NULL;
if(IsEmpty()) {
keyPtr = NULL;
}else {
*keyPtr = first->key;
if(yield_type == 1)
currentThread->Yield();
//the 1th positon of yield ****(1)
//the returned tempitem may not the item we just removed!
//the item maybe incorrect
tempitem = first->item;
temp = first;
first = first->next;
if(first != NULL) {
if(yield_type == 2)
currentThread->Yield(); //the 2th positon of yield ****(2)
//the dllist may lose its item
first->prev = NULL;
}else last = NULL;
}
delete temp;
return tempitem;
}
//----------------------------------------------------------------------
// DLList::SortedInsert
//
//
Insert an "item" into a dllist, so that the dllist elements are
sorted in increasing order by "sortKey".
//----------------------------------------------------------------------
void
DLList::SortedInsert(void *item, int sortKey)
{
DLLElement *element = new DLLElement(item, sortKey);
DLLElement *ptr;
// keep track
if (IsEmpty()) { // if list is empty, put
first = element;
if(yield_type == 3)
currentThread->Yield(); //the 3th positon of yield ****(3)
//the thread may make a mess of the dllist
last = element;
} else if (sortKey < first->key) {
// item goes on front of list
element->next = first;
first->prev = element;
first = element;
} else {
// look for first elt in list bigger than item
ptr = first;
while((sortKey >=
ptr->key)&&(ptr->next != NULL))
ptr = ptr->next;
if (sortKey < ptr->key) {
(ptr->prev)->next = element;
element->prev = ptr->prev;
element->next = ptr;
if(yield_type == 4)
currentThread->Yield(); //the 4th positon of yield ****(4)
//the dllist may be broken by multi-threads
ptr->prev = element;
}else {
ptr->next = element;
element->prev = ptr;
last = element;
}
}
}
c)dllist-driver.cc (~/nachos/nachos-3.4/code/threads/)
链表驱动函数 Insert,Remove
void Insert(int N,DLList *dllist,int TdNo)
//Insert N items into the dllist.
{
int rd;
srand(time(0));
for(int i=0;iSortedInsert(NULL,rd);
printf("Thread %dth have inserted the item
(key=%d)\n",TdNo,rd);
}
}
void Remove(int N,DLList *dllist,int TdNo)
//Remove N items from the dllist.
{
int keyword;
for(int i=0;iRemove(&keyword);
printf("Thread %dth have removed the item (key=%d)\n",TdNo,keyword);
}
}
d)线程测试 threadtest.cc (~/nachos/nachos-3.4/code/threads/)
其中我们设置了几个全局变量,testnum,threadnum,N,lb1_cor_type,dllist。
testnum:测试号,对应相应的测试函数
threadnum:线程数,通过命令行参数录入
N:用于驱动函数插入或者删除链表项的控制
lb1_cor_type:用于指示不同切换位置可能出现的错误种类,通过命令行参数修改
dllist:用于线程操作的公共链表
我们在 threadtest.cc 中添加了新的测试函数 ThreadTest2,在其中,我们创建了
threadnum 个线程,每个线程都执行相同的操作,插入 N 个项,之后删除 N 个项,在这
里通过调用 DLListThread 函数实现。同时在进行这些操作的时候打印出相应的提示信
息。
void
DLListThread(int n)
{
}
Insert(N,dllist,n);
Remove(N,dllist,n);
void
ThreadTest2()
{
DEBUG('t', "Entering ThreadTest2");
dllist = new DLList(lb1_cor_type);
for(int i = 1; i < threadnum; i++) {
Thread *t = new Thread("forked thread");
t->Fork(DLListThread, i);
}
DLListThread(threadnum);
}
e)main.cc (~/nachos/nachos-3.4/code/threads/)
在 main 函数中,我们需要做的并不多,只需要将相应的参数处理即可。
for(argc--, argv++; argc > 0; argc -= argCount, argv += argCount) {
argCount = 1;
switch(argv[0][1]) {
case 'q':
testnum = atoi(argv[1]);
argCount++;
break;
case 'T'://线程数
threadnum = atoi(argv[1]);
argCount++;
break;
case 'N'://插入或删除项数
N = atoi(argv[1]);
argCount++;
break;
case 'M'://指示切换位置
lb1_cor_type = atoi(argv[1]);
argCount++;
break;
default:
testnum = 1;
break;
}
}
f)Makefile.common (~/nachos/nachos-3.4/code /)
在 THREAD_H 下添加:../threads/dllist.h\
在 THREAD_C 下添加:../threads/dllist.cc\
../threads/dllist-driver.cc\
在 THREAD_H 下添加:dllist.o dllist-driver.o
4.编译执行
在~/nachos/nachos-3.4/code/threads/下执行:make depend,然后执行 make 进行编译。
等编译成功后,执行:./nachos [-q 1|2] [-T t] [-N n] [-M m],其中带有[ ]为可选参数,参数
与顺序无关。
-q:选择测试函数
-T:设置线程数
-N:设置驱动函数的控制参数
-M:选择切换位置(位置选择不同出项的错误可能不同)
五、运行结果和错误分析
1.正常运行结果
1)执行:./nachos –q 1
2)执行:./nachos –q 2 –T 1 –N 3 (只有一个线程的情况)
2.错误出现
1)执行:./nachos –q 2 –N 2 –T 2 –M 1
出现第一类错误:插入与移出的元素不一样,插入 88,19,而移出为 19,19。(颜色
标记处)
2)执行:./nachos –q 2 –N 2 –T 2 –M 2
出现第二类错误:链表失去了自己的元素,插入 91,16,而只能移出 16,另一元
素 91 丢失。(颜色标记处)
3)执行:./nachos –q 2 –N 2 –T 2 –M 3
出现第三类错误:链表在被多个线程访问时其中的元素被打乱,一个线程插入的元素可
能被其他移出。(颜色标记处)
4)执行:./nachos –q 2 –N 3 –T 3 –M 4
出现第四类错误:链表被彻底损坏。(颜色标记处)