IPC信号量当中的UNDO问题

来源:互联网 发布:python 最大回撤 编辑:程序博客网 时间:2024/05/29 18:10

转自:
http://blog.donews.com/quickmouse/archive/2009/04/19/1487037.aspx

    以往使用信号量的情形通常都是用于进程间互斥,锁定临界区。以前在维护bbs的代码过程当中,学会了用文件锁flock,后来拜读APUE学会了信号量,信号量还是比文件锁有优势的,效率不是高那么一点,起码不用打开文件关闭文件这些耗时间的工作。但是信号量最大的一个问题就是一旦锁定,若在解锁之前出现程序崩溃等segment fault问题,将直接导致锁定的信号量无法恢复,形成永久占用。文件锁则没有这个问题,进程的退出将导致文件描述符关闭,在该描述符上进行的锁定操作就自行解除了。
    好在设计信号量的前辈们考虑到了这个因素,在加锁的时候会有一个UNDO的设置,也就是在调用semop的时候指定操作结构体当中可以放置一个UNDO参数,我通常都是这样去调用的:
semop(iSemID, &stLocksem, 1);
    其中stLocksem就是定义的一个操作结构体,一般定义为:
static struct sembuf stLocksem={0, -1, SEM_UNDO}, stUnlocksem={0, 1, SEM_UNDO};
    这样的UNDO选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值。详细的可以参考man pages。

    今天想要记录的内容不是来谈这个锁定的。前面也提到了,信号量通常用于锁定临界区,但还有一个重要的工作,也是信号量常干的活——同步进程(线程)。以往我自己写的程序,比较少涉及这方面的工作,最近演了这么一出,在这个信号量锁定上就“老革命碰到新问题了”。
    程序刚开始运行时,挺好。若干次同步后,程序报锁定失败,semop返回-1,errno返回值ERANGE(34),就是number out of range。在每次锁定解锁之前都用semctl检查信号量的值,始终保持0/1的范围,很正常。百思不得其解之间,把眼光盯向了SEM_UNDO,琢磨良久,才估计可能是由于UNDO造成的,试着把前述结构体当中的SEM_UNDO去掉,一切又都ok了。

    原来对于SEM_UNDO来说,内核记录的信息是跟进程相关的。一个进程在lock的时候设置一个UNDO,那么对应该进程的UNDO计数就多一个,unlock的时候设置一个UNDO,那么计数就减一个。对于临界区互斥的应用而言,lock和unlock都是在一个进程当中完成,于是UNDO可以切实发挥作用。然而,用在同步进程(线程)里,由于是生产者-消费者模型,一边不停的lock,另一边不停的unlock,那么使用UNDO就不起作用了,而且由于都是单边操作,导致UNDO计数对单一进程而言,只朝一个方向发展,最后必定是超过内核限制值,当然出现了ERANGE的错误。
    接着,为了验证这个问题,单独写了一对小程序加解锁并显示锁定次数值。果然在次数达到32768时候,出现ERANGE错误,证实此事。