大头
Table_bottom

标签云
Table_bottom

分类
Table_bottom

日历
十一月
272829303112
3456789
10111213141516
17181920212223
24252627282930
Table_bottom

评论
Table_bottom

留言
Table_bottom

微博
Table_bottom

热门文章
Table_bottom

随机文章
Table_bottom

豆瓣上谁关注这里
Table_bottom

链接
Table_bottom

搜索栏
Table_bottom

RSS
RSS Link
Table_bottom

功能
Table_bottom

页面
Table_bottom

计数器
482022
Table_bottom

访客统计
Table_bottom

存档
Table_bottom

代码之美 - 081003

收到了《代码之美》,把目录翻来翻去,在33章里找我最容易能看懂的,似乎并没有哪一章让我觉得可以顺畅阅读不用多加以思索的。

最后挑选了第24章《美丽的并发》,作者是Simon Peyton Jones,他用Haskell语言来介绍STM(Software transactional memory),看得我很头大。注意力几乎全被Haskell语言占据了,3、5行的程序段却费了不少脑筋去思量。以至于文章最后作者在总结的时候,我都没有感觉到他说的所有的好处到底在哪里。回头想想,似乎是那么回事,可一到细节,脑子里搅和的还是语言的细节。

反正我记住了,最大的好处是使并发程序的模块性更好。还避免了不少常犯的错误。

这一章里的代码有下载

我还想知道,Haskell多用在什么地方?记录下Haskell的官方网站,那里可以找到很多信息,下载很多东西。

再记下这本书的英文版勘误信息

 

信号量和同步互斥

进程的互斥与P、V操作

一、临界资源

什么是临界资源:任何时候只允许一个进程使用的资源为临界资源。

什么是临界区:访问临界资源的代码段为临界区。

例如:
代码段1 
 

  1. a = count;
  2. a--;
  3. count = a;

 代码段2   

  1. b = count;
  2. b++;
  3. count = b;

为临界区,count为临界资源。

对临界资源的访问必须满足以下条件:
一次只能有一个进程进入,其他进程等待。
进入者必须在有限时间内退出。
等待者应该有机会进入。

二、信号量

信号量的结构模型:(S,Q)
在2.4.x内核中,信号量数据结构定义为(include/asm/semaphore.h): 
 

  1. struct semaphore {
  2. atomic_t count;
  3. int sleepers;
  4. wait_queue_head_t wait;
  5. #if WAITQUEUE_DEBUG
  6. long __magic;
  7. #endif
  8. };

信号量操作:
1) 初始化
2) P操作
a) S--
b) if(S < 0) 在Q队列中睡眠
     else 进入
3) V操作
a) S++
b) if(S <= 0) 唤醒一睡眠进程(等待队列中有进程在睡眠)
     else 继续
P操作可以理解为申请资源,V操作可以理解为释放资源,在2.4.x内核semaphore结构中,count是资源计数,为正数或0时表示可用资源数,-1表示没有空闲资源且有等待进程,至于等待进程的数量并不关心,实现中依赖了sleepers,比较复杂,但好处是是up操作非常简单,只需汇编原子地将count加1,如果小于等于0表示有进程等待,调用__up_wakeup() --> __up(),否则返回。linux中的实现可以到arch/i386/kernel/semaphore.c中看到(2.4.x kernel),__down()和__up()。

信号量实现:
用ITRON的一个简单实现<An ITRON Implementation>做为例子。我粗略地看了一下代码,关于信号量(semaphore)的P/V操作,有计数器控制和等待队列。
SEMCB(semaphore control block)结构中有id,队列,计数器等成员。 

  1. OS_AcquireSemaphoreTimeOut()
  2. { ...
  3.         lock()
  4.         semcb->semcnt-- // counter --
  5.         if(semcb->semcnt < 0)
  6.         {
  7.                 make_wait() // make current task in wait state
  8.                 queue_insert_tpri() // insert by priority to the semaphore's
  9.                 //wait queue,which is a circular queue
  10.         }
  11.         unlock()
  12.         ...
  13. }
  14.  
  15. OS_FreeSemaphore()
  16. { ...
  17.         lock()
  18.         if(semcp->semcnt < 0)
  19.         {
  20.                 wait_release_ok()
  21.                 |
  22.                 -> wait_release() // release a task from wait state
  23.         }
  24.         semcb->semcnt++ // counter ++
  25.         unlock()
  26.         ...
  27. }

注:V操作中S先加1再判断是否小于等于0和先判断是否小于0再加1是等价的。
注:在这个实现中,semcnt值不仅正负分别表示资源空闲或有进程等待,其绝对值也是有意义的,为负时值代表等待的进程数,为正或零时值代表空闲的资源数。

三、同步互斥模型

S=1

  1. P(S)
  2. 临界区
  3. V(S)


  1. P(S)
  2. 临界区
  3. V(S)


生产者消费者问题
(生产者计算生成数,消费者打印之。)

1个生产者1个消费者,缓冲buf为1
2个信号量实现模型:put = 1 get = 0

  1. 计算x
  2. P(put)
  3. buf = x
  4. V(get)

  1. P(get)
  2. y = buf
  3. V(put)
  4. 打印y


1个生产者2个消费者,缓冲buf为1(2个消费者一个打印奇数一个打印偶数)
3个信号量实现模型:put = 1 getj = 0 geto = 0

  1. 计算x
  2. P(put)
  3. buf = x
  4. if(x为奇)
  5.     V(getj)
  6. else
  7.     V(geto)

  1. P(getj)
  2. y = buf
  3. V(put)
  4. 打印y

  1. P(geto)
  2. y = buf
  3. V(put)
  4. 打印y

这个模型比较直观,但是用了3个信号量。在计算出x后判断其奇偶再"V"不同的信号量。
如果只使用2个信号量如何实现,生产者计算出x后直接"V"一个信号量,2个消费者取出后判断是否打印还是不于理会。
2个信号量实现模型:put = 1 get = 0

  1. 计算x
  2. P(put)
  3. buf = x
  4. V(get)

  1. P(get)
  2. y = buf
  3. if(y为奇)
  4. {
  5.     V(put)
  6.     打印y
  7. }
  8. else
  9.     V(get)

 C

  1. P(get)
  2. y = buf
  3. if(y为偶)
  4. {
  5.     V(put)
  6.     打印y
  7. }
  8. else
  9.     V(get)


1个生产者1个消费者,缓冲buf为m
2个信号量实现模型:put = m get = 0

  1. 计算x
  2. P(put)
  3. buf[t] = x
  4. t = (++t) % m
  5. V(get)

 B 

  1. P(get)
  2. y = buf[k]
  3. k = (++k) % m
  4. V(put)
  5. 打印y


N个生产者M个消费者,缓冲buf为m
4个信号量实现模型:put = m get = 0 T = 1 K = 1

  1. 计算x
  2. P(put)
  3. P(T)
  4. buf[t] = x
  5. t = (++t) % m
  6. V(T)
  7. V(get)

  1. P(get)
  2. P(K)
  3. y = buf[k]
  4. k = (++k) % m
  5. V(K)
  6. V(put)
  7. 打印y

 
四、删除一个信号量

删除一个信号量,系统应该释放一些资源。如果无进程在等待此信号量,处理比较简单。
如果有进程在等待此信号量,如何处理这些进程,有人说kill,但我看到的一个实现是唤醒。
除进程之外,这个等待队列也需要释放,否则会造成内存泄露。
不仅是删除一个信号量,在系统中当删除某个资源时,会释放等待队列的所有task。

OS_DelFlag --|
OS_DelMessageBuffer --|--> call wait_delete(QUEUE *) --> OS_DelSemaphore --| 

  1. while()
  2. {wait_release();...} // release all tasks blocked on specified wait queue


五、由来

为什么敲打这么个东西。因为有个自主实现的基于ITRON的OS需要测试。在测试其信号量资源时我补充了一个测试用例,即当有进程在等待一个信号量时删除此信号量系统会有如何反应。基于此,先给测试人员做了presentation介绍信号量及同步互斥模型。然后粗略地看了看linux 2.4.x,ITRON和ucLinux三个系统的P/V操作和删除信号量的实现。因工作繁忙没有深入研究,简单整理一下用了两个小时边解bug边偷闲着输入完。然后steedhorse帮忙做了检查,经过了一个小时的讨论,改了一些错误,在此感谢。
有的问题我不是很清楚,希望看了后能提出来大家讨论。