进程间通信——信号量、互斥锁等的异同

2019-07-13 07:17发布

进程间通信——信号量、互斥锁等的异同

最早接触在系统上编程,是在嵌入式Linux上完成几项功能。当时就是按照写单片机程序的思维写的。实现几个功能,就用了一个进程,单线程来做。 后来实习时,接触Linux下多进程,多线程的编程。了解到多个线程之间的操作需要进行同步,互斥等。 在工作后,有进行了uCOS的多任务编程。发现uCOS中的多任务的同步,互斥与Linux下有很多相似性。但是也有很多不同。 也因为一开始把uCOS的信号量,互斥锁等和Linux类比来操作。概念确实是一致的,但是由于具体实现的差异,也导致了实际操作中的一些失误(uCOS中的任务相当于进程中的线程,任务间通信相当于线程间通信)。 虽然,经过这些实践,但是有些疑问却一直没有解开。
在一篇博客中到关于深入了解系统的书籍推荐找到了《UNIX网络编程第二卷:进程间通信》,说道它介绍了信号量等IPC通信(但是它也说了,这些技术是老掉牙的技术)。 不过这本书确实解开了不少一直来的疑惑。
传统设计原则认为:把应用程序设计为一组互相通信的小片段比将其设计为单个庞大的程序更好。 应用程序的几种构建方法: 1、用一个庞大的程序完成全部工作。 2、使用多个程序,程序之间用某种形式的IPC进行通信; 3、使用一个包含多个线程的程序,线程之间使用某种IPC。 注:IPC:Interprocess Communication,进程间通信。 在自己最早的时候使用的就是第一种方式来构建程序的。
对于UNPv2,主要介绍了4种不同的IPC方式: 1、消息传递; 2、同步(互斥量、条件变量、读写锁、文件和记录锁、信号量); 3、共享内存; 4、远程过程调用。
由于更多的是在单进程中实现多线程通信,对于同步的使用要多于消息传递。因为在单进程中,所有线程共享运行地址空间。全局变量(以及局部静态变量(通过访问函数))所有线程可以访问。所以对于同步的使用要多于消息传递。也因此对于互斥量,信号量,条件变量等何时使用,如何使用留下了疑问。 一开始,自己把互斥锁和信号量几乎没有怎么区分,只要实现了对临界区的保护就可以。也感觉他们之间似乎没什么差异,还纳闷为什么会有了互斥锁还有信号量存在。 在已经有了互斥锁的时候,还提供信号量,Posix.1给出的理由是:提供信号量的主要目的是提供一种进程间同步方式。这些进程可能共享也可能不共享内存区。互斥锁,条件变量是作为线程间同步机制说明的,这些线程总是共享某个内存区(因为线程共享运行地址空间)。这两者都是已经广泛用了很多年的同步范式。每组原语都特别适合于特定的问题。 应该可以理解为,互斥锁,条件变量,信号量都可以作为同步来使用,不必纠结于需要同步时使用那一个。但是每一个都用了很长时间,都有自己特别适合的问题。所以就都留着了。 也就因为有自己最合适的问题,所以它们之间肯定有些地方很不一样。 以下是:信号量,互斥锁,条件变量三者的一些差别: 1、互斥锁总是要由给它上锁的线程解锁,信号量的挂出却不必由执行过它的等待操作的统一线程执行。 2、互斥锁要么被锁住,要么被解开(二值状态,类似于二值信号量,区别于多值信号量)。 3、既然信号量有一个与之关联的状态(它的计数值),那么信号量挂出操作总是被记住。而当向一个条件变量发送信号时,如果没有线程等待在盖条件变量,则该信号丢失。(即:如果在信号量挂出的时候,没有线程等待此信号量,则在下次有线程等待的时候,会收到此信号量,并进行处理。而条件变量则将会陷入等待) 4、在Posix.1标准中,各种同步技巧(互斥锁,条件变量,读写锁,信号量)中,只有信号量挂出操作,能够在信号处理程序中安全调用。 从这几点看,似乎信号量很有优势,但也因为功能强大,所以其运行开销会偏大。具体的性能,需要实际在系统中测试才知道。
UNPv2在最后关于解决问题使用哪种IPC给出的解答是:对于解决特定问题使用哪种IPC,应该视具体问题而定。我们可以做的是,熟悉各种IPC机制,然后根据具体需要选择一个适合当前的。
关于消息传递一块,个人感觉:远程过程调用和共享内存,都是实现了一种消息传递的方式,或者更准确的说是数据传递。虽然对于具体实现技术上来说,有四种IPC方式,但是从数据传递和同步来看,只有两种。一种实现临界区保护,一种实现信息的传递。
在uCOS中也有一些对应于这些IPC方式的实现(当然是任务间通信): 1、消息传递(邮箱,消息队列); 3、同步(信号量,互斥锁,邮箱(可以作为互斥来用)); 由于uCOS中的代码,共享内存空间,所以全局变量就相当于内存共享。所以在使用uCOS的时候,更多的参照线程间通信的方式,来操作。但是就像文前提到的,uCOS毕竟不遵守Posix.1标准,所以在实现上不一样。 在Linux上信号量很多时候是二值信号量(不知道最新的是否支持多值),但是uCOS支持的信号量是多值信号量。由于一开始将uCOS上的信号量类比于Linux上的信号量,也当做二值来处理,结果程序的运行结果不符合预期。
虽然很多概念是适用的,但是当针对于具体应用的时候,还是要看具体的实现是怎么样子的。这样才会避免一些细节上的问题。而且对于嵌入式编程而言,IPC技术不但不是老掉牙的技术,相反还是十分重要的。因为嵌入式开发中,有时候使用的操作系统,是共享地址空间的。而且有时候,要实现多任务,多线程等同步操作就是其中实现任务或者线程间协作的很重要的一个步骤。即使开发裸机系统,出去性能考虑会实现为异步式的。而异步就要考虑很多资源的同步问题,其实也就是做一个操作系统的一些工作——调度。
由于现在Linux等操作系统对于这些高度抽象,似乎不用去考虑这些。但是对于嵌入式编程而言,这些还是要经常面对的问题。 IPC对于嵌入式开发,还不过时。
注: http://coolshell.cn/articles/4990.html
技术员技术练级攻略 《UNPv2》 嵌入式实时操作系统μC/OS-2