以文本方式查看主题

-  中文XML论坛 - 专业的XML技术讨论区  (http://bbs.xml.org.cn/index.asp)
--  『 计算机考研交流 』   (http://bbs.xml.org.cn/list.asp?boardid=67)
----  关于北大操作系统教程第四章35题的讨论  (http://bbs.xml.org.cn/dispbbs.asp?boardid=67&rootid=&id=39456)


--  作者:ychj
--  发布时间:10/28/2006 3:16:00 AM

--  关于北大操作系统教程第四章35题的讨论
关于银行业务的问题。
想完美地解决这道题,似乎有点麻烦。
关键是需要满足以下两个问题:
1) 进入银行的顾客可以直接取号,不需要等待;
2) 空闲的柜员叫号时不能叫错人,即必须要求先来接受服务的是当前的最小号。
不知道大家有没有很好地解决这道题?想圆满地解决,似乎并不容易。
--  作者:ychj
--  发布时间:10/28/2006 5:41:00 AM

--  
大家考虑得怎么样啊?
我已经想到一个绝妙的办法了,呵呵。
--  作者:ychj
--  发布时间:10/28/2006 6:01:00 AM

--  
这个问题是n个理发师问题的变种,但比其更加复杂。
因为理发师问题并没有苛刻地要求先来的顾客(即先领号的)一定先接受服务;而银行叫号都是从小号到大号的。

--  作者:apolor
--  发布时间:10/28/2006 8:50:00 PM

--  
int count_s = 0;
int count_c = 0;
semaphore customer;//初值为0
semaphore mutex_s;//初值为0
semaphore mutex_c;//初值为0
semaphore staff;//初值为n

void Staff(i){
        while(ture){
                P(customer);
                P(mutex_s);
                        count_s ++;
                        叫编号为count_s的顾客;
                V(mutex_s);
                服务;
        }
}

//////////////////////////////////////////////////////////

void Customer(){
        P(mutex_c);
                count_c ++;
        V(mutex_c);
        V(customer);
        P(staff);
                接受服务;
        V(staff);
        离开;
}


--  作者:Supremgoooo
--  发布时间:10/28/2006 9:54:00 PM

--  
apolor的算法正确。另外我认为count_c可以去掉。

这个问题很像理发师问题,但是它没有进屋人数的限制。它叫做面包师问题,由Lamport(注:Lamport对分布式os有重大贡献)在1974年提出。


--  作者:apolor
--  发布时间:10/28/2006 10:13:00 PM

--  
以下是引用Supremgoooo在2006-10-28 21:54:00的发言:
另外我认为count_c可以去掉。

count_c是顾客的编号,是一定要要的。


--  作者:Supremgoooo
--  发布时间:10/28/2006 10:55:00 PM

--  
不好意思!刚才想的有问题,不能省!

--  作者:ychj
--  发布时间:10/29/2006 2:19:00 AM

--  
所谓银行叫号就是要让先拿号的顾客一定先得到服务。
请大家仔细分析一下代码,存在以下问题:
(1) 上面这段代码有这种可能性:i+1号顾客在i号顾客之前进入Staff等待队列,从而先得到服务,这显然是有问题的。
(2) 服务于接受服务之间缺乏同步。例如:Customer也许已经执行完“接受服务”了,Staff可能才开始执行“服务”。
(3) 当Customer进程唤醒Staff进程后,可能Staff还没开始叫号,而Customer已经接受完服务并离开了,这都是不合理的。
这是上述算法的明显问题,大家想想有什么好的解决方案。



--  作者:ychj
--  发布时间:10/29/2006 2:23:00 AM

--  
前沿教研室编写的考研丛书中的理发师算法也是有一些问题。

--  作者:ychj
--  发布时间:10/29/2006 2:29:00 AM

--  
另外,大家发现没有:
Customer进程的V(Staff)其实在某些时候已经具备“叫号”功能了,而Staff进程也在叫号。
逻辑混乱。

--  作者:Supremgoooo
--  发布时间:10/29/2006 11:18:00 PM

--  
这句话很有道理,但是很遗憾,它是无法实现的。

我在前面反复说过,P,V不能实现先来先服务。这个是由于P,V本身的随机性导致的。

对随机性的理解:
(1)多进程并发执行时的相对速度在执行前不能做任何假设。
(2)在一组进程执行之后,它们刚才的执行速度不可能重现。
(3)V的定义是从等待队列中随机的释放一个进程。
(4)一个进程的执行也受到其它进程不可估计的干扰。

以上是理论。我在这方面曾经专门研究过,先来先服务并不是绝对不行的,我提供两种方法:
(1)按照写者优先思想,一个进程一个队列,n个进程设置成为n个队列。该算法比较容易理解,但是几乎是无法编成实现的。
(2)引入队列,一个进程被阻塞到队列尾,释放时释放队列首进程。这似乎有些不伦不类,已经不像是P,V题了。

UNIX中有过所谓的Hard semaphore的概念,说V时释放队首进程。这主要可能是为实时系统提供的,而且它似乎从没有被实现过。所有基于POSIX标准的os几乎都是采用weak semaphore,这与人们对实时系统本质的理解有关。“实时系统中其实可能不存在抢占”。

以下是引用ychj在2006-10-29 2:19:00的发言:
所谓银行叫号就是要让先拿号的顾客一定先得到服务。
请大家仔细分析一下代码,存在以下问题:
(1) 上面这段代码有这种可能性:i+1号顾客在i号顾客之前进入Staff等待队列,从而先得到服务,这显然是有问题的。
(2) 服务于接受服务之间缺乏同步。例如:Customer也许已经执行完“接受服务”了,Staff可能才开始执行“服务”。
(3) 当Customer进程唤醒Staff进程后,可能Staff还没开始叫号,而Customer已经接受完服务并离开了,这都是不合理的。
这是上述算法的明显问题,大家想想有什么好的解决方案。





--  作者:citeator
--  发布时间:10/30/2006 11:58:00 AM

--  
临界区是 staff提供的服务,一次只能一个customer享受。
先是要搞清楚临界区是哪个。
其他的好像不麻烦。


--  作者:Supremgoooo
--  发布时间:10/30/2006 10:15:00 PM

--  
我给出apolor算法的改进算法。
//////////////////////////////////////////////
int count_s = 0;
int count_c = 0;
semaphore customer;//初值为0
semaphore mutex_s;//初值为0
semaphore mutex_c;//初值为0
semaphore staff;//初值为0
semaphore begin;//初值为0

void Staff(i){
        while(true){
                P(customer);
                P(mutex_s);
                        count_s ++;
                        叫编号为count_s的顾客;
                V(mutex_s);
                V(staff);
                P(begin);
                服务;
        }
}

//////////////////////////////////////////////////////////

void Customer(int &i){
        P(mutex_c);
                i=count_c ++; //i表示自己的号码
        V(mutex_c);
        V(customer);
L:     P(staff);
        if(check(i)) //看一下是否叫的是自己,是则执行;否则让权   
        { V(begin);被服务;}
        else
        { V(staff);goto L;}
        离开;
}
注:这个算法虽然有缺点,但是解决了ychj的问题。


[此贴子已经被作者于2006-10-31 8:59:42编辑过]

--  作者:ychj
--  发布时间:10/31/2006 12:59:00 AM

--  
"(3)V的定义是从等待队列中随机的释放一个进程。" 这句话感觉有问题,既然是用队列存储,肯定是按先进先出的顺序。
(1) Supremgoooo说的对,的确是要引入一个队列;
(2) 同时引入一个共享变量next,记录下次首先接受服务的顾客的编号(因为go to语句有害,所以Java,C++里应该都没有了吧,尽量不使用它);
(3) 另外,需要一个信号量数组,它恰好实现了两个功能:
     (a) 当一个顾客先进入服务,他可以唤醒下一个号顾客进程;
     (b) 当顾客服务结束后,Staff对相应的顾客进程做V操作,告诉他服务结束了。

我个人觉得,PV操作中既然可以用数组,用队列是没有什么问题的,并且都是伪码级的。
大家觉得呢?


--  作者:Supremgoooo
--  发布时间:10/31/2006 9:12:00 AM

--  
先进先出违反了随机性原则,在绝大多数的os中是不被采用的。

另外:北大的教材和Tanenbaum的教材上的V定义均是随机释放。

以下是引用ychj在2006-10-31 0:59:00的发言:
"(3)V的定义是从等待队列中随机的释放一个进程。" 这句话感觉有问题,既然是用队列存储,肯定是按先进先出的顺序。
  


--  作者:Supremgoooo
--  发布时间:10/31/2006 9:17:00 AM

--  
想了一下,13楼的算法虽然使用的是busywaiting软件解法的思想,但是它问题不大。算是这个题的正确解法了。

如果谁还对busywaiting这种方式不满,可以采用我给的先来先服务思想解决这个问题。欢迎跟帖讨论。


--  作者:mxf3306
--  发布时间:10/31/2006 4:20:00 PM

--  
shared variable
Choosing: array[1..N] of 0..1 initially 0;
Number: array[1..N] of 0..cx~ initially 0
process p: /* 1 < p < N */
private variable
q: 1..N
while true do
0: Noncritical Section;
1: Choosing[p] := 1;
2: Number[p] := 1 + max{Number[l], ..., Number[N]};
3: Choosing[p] := 0;
4: for q:=l to N skip p do
5: await Choosing[q] = 0; /* busy wait */
6: await Number[q] = 0 V (gumber[p],p) < (gumber[q],q) /* busy wait */
    od;
7: Critical Section;
8: Number[p] := 0
od
(In line 6, the notation (a, b) < (c, d) is a shorthand for (a < c) V (a = c A b < d).)


[此贴子已经被作者于2006-10-31 16:52:01编辑过]

--  作者:mxf3306
--  发布时间:10/31/2006 4:34:00 PM

--  
以上就是最经典的bakery algorithm的阐述形式。在网上查了很久,我还没找到它的PV实现形式。我觉得,PV的优点在于它能有效地避免不必要的busy waiting。而且这个学期,北大的实习里就有这么一题,我很怀疑supremgoooo的算法是否能直接付诸实践,许多进程在不停的CHECK,效率会极其低下。
所以我觉得可能会有更好的算法,但在这个题目上面已经花了太多时间了,所以先搁置下来吧。如果考试考到的话,也只能按照supremgoooo的算法了。


--  作者:mxf3306
--  发布时间:10/31/2006 4:38:00 PM

--  
关于FCFS 我在ACM一篇文章(Lamport on Mutual Exclusion: 27 Years of Planting Seeds)中找到这句话:The bakery algorithm is also of interest because it satisfies the following rather strong progress property.
• First-come, First-served (FCFS) Priority: If process p enters the bakery before q enters the doorway,then p executes its critical section before q.

--  作者:ychj
--  发布时间:11/1/2006 2:02:00 AM

--  
查了一下,不同的操作系统对于信号量队列的处理是不一样的,分为强、弱两种设计方法。强者在作V操作时唤醒进程是按先进先出的。因此在这里我们肯定必须考虑先进先出(否则没法做),其实在操作系统实现上,我们也完全可以简单地实现先进先出的信号量队列。
这道题的解法我几天前已经完全做出了,暂时先不告诉大家的源代码实现。
只是告诉大家几个关键的地方,大家想想看,因为只有自己经过认真思考后才有可能长进,呵呵。
(1) 引入一个队列;
(2) 声明一个信号量next,用于指示下一次将首先接受服务的顾客编号;
(3) 一个信号量数组wait[0...m-1],m为一天接受服务的顾客人次的最大值;
这里需要的一种思路就是,由于领号之后顾客进程进入同一个信号量的队列(比如counter)的顺序是随机的,因此不能让这些进程进入一个信号量等待,这是关键。
当然另外还需要一些信号量,比如counter、ready 以及几个互斥信号量等等。
这种做法应该说是趋于完善的,因为它接近于当你实际编程时需要考虑的各种细节问题(当然实际编程时还要更复杂);而上述大家提出的算法其实都是很粗糙的,并且在实际编程时离实现还有很远的距离。
当你设计的一个算法,不管它是PV的伪代码形式还是实际代码,我想都应该是没有漏洞的才行。
比如:
Customer与Staff的进程里对于一个同时发生的行为“理发”与“接受理发”,我们只能用文字说明的形式去告诉别人,这两个行为是同时发生,是一个行为的两个角度而已。
但是当你用程序来运行时,程序是不知道的,这样的程序,一运行肯定就错了。
因此,更加完善的设计方法是需要我们考虑的。
当然,若有一种情况,大家则不需要考虑这些,就是如果老师不要求我们掌握那么深入,呵呵。


W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
4,009.766ms