WebRTC源代码探索之旅——多线程篇(5 - 3)

来源:互联网 发布:数据库流程图怎么画 编辑:程序博客网 时间:2024/05/18 16:38

.4 talk_base::PosixSignalHandler

 

talk_base::PosixSignalHandler和talk_base::PosixSignalDispatcher这两个类只有Linux版本。并且在整个WebRTC的源代码中没有任何地方使用过这两个类。因此,对它们的代码分析主要是为了帮助Windows开发人员从实用的角度学习如何使用Linux平台下的部件。

 

talk_base::PosixSignalHandler类主要实现了将Linux的Signal机制纳入到多路分离器的架构中去。对于Windows开发人员来说,Signal机制是一个比较陌生的东西。而且对它的处理比较麻烦。因为Signal会在程序运行的任何时候出现,一旦触发就会调用注册的处理函数,开发人员没法假定这时程序中哪些工具是否可用。正如在talk_base::PosixSignalHandler::OnPosixSignalReceived函数的注释中所说的在出错的时候我们甚至无法记录log:

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. // Nothing we can do here. If there's an error somehow then there's  
  2. // nothing we can safely do from a signal handler.  
  3. // No, we can't even safely log it.  


 

此外,Linux的有些signal相关的函数在不同版本的Linux/Unix平台以及的表现完全不同。因为,signal机制是一个古老的历史遗留问题,在当年Unix主导一切,各个大公司又各自为战的年代,要提供一个公认完备的标准确实不易。幸运的是从WebRTC的代码来看,几乎没有什么组件使用了signal机制,那就说明绝大多数的现代程序都是可以不使用signal机制就能实现自己想要的功能的。

 

首先,让我们来看一下talk_base::PosixSignalHandler类的工作原理。它被伪装成了一个singleton,而它其实是一个全局唯一的对象,创建后永不释放,直到程序退出时内存泄露。注意,这不是开发人员不小心泄露了内存,而是主动的泄露了内存。在WebRTC中有一个专用的宏(LIBJINGLE_DEFINE_STATIC_LOCAL)用来定义定义这种类实例。如果,在程序中有限的几个类实例被设置成LIBJINGLE_DEFINE_STATIC_LOCAL理论上来说是没有什么负面效果的。因为它不会造成程序在运行期间不断地积累内存泄露直到拖垮整个系统。但是使用这种手法依然需要谨慎。在使用talk_base::PosixSignalHandler的时候,talk_base::PhysicalSocketServer会将 talk_base::PosixSignalHandler::OnPosixSignalReceived函数通过sigaction注册到系统以响应感兴趣signal。当系统发出被监听的signal后OnPosixSignalReceived函数会被调用。该函数会在成员变量received_signal_(数组)中相应的位置上设置为true,并在pipe中写入1个字节的数据(是的,它的核心工作原理和talk_base::EventDispatcher是一样的),这样就能解锁阻塞等待在select函数上的talk_base::PhysicalSocketServer。talk_base::PhysicalSocketServer通过talk_base::PosixSignalHandler::IsSignalSet函数来检查received_signal_数组以确定哪个signal被激活,并调用相应的处理函数。

 

在talk_base::PosixSignalHandler中主要使用的Linux API包括:

pipe:创建管道

 

fcntl:设置文件描述符的选项;该函数在构造函数中被调用,将新创建的一对pipe文件描述符设置为非阻塞。调用代码为:fcntl(afd_[0], F_SETFL, O_NONBLOCK)。不过,根据EventDispatcher构造函数的代码来看,这一步好像没有必要。考虑到EventDispatcher是一个被重度使用的对象,可以确信没有必要把pipe的文件描述符设置为非阻塞(pipe默认应该就是阻塞的,并且在绝大多数系统中IO默认都是阻塞的)。

 

read:从管道读出数据,解除管道文件描述符的可读状态(该函数在talk_base::PosixSignalDispatcher中使用)

 

write:向管道写入数据,以解锁阻塞的select函数

 

close:关闭管道

 

sigaction:将signal处理函数注册到系统,当signum指定编号的signal触发时,系统会调用相应的处理函数(该函数在talk_base::PhysicalSocketServer::InstallSignal中使用)

 

5.5 talk_base::PosixSignalDispatcher

 

该类也是Linux独有的一个类,主要是作为代表talk_base::PosixSignalHandler的分发器,通过将该对象添加入talk_base::PhysicalSocketServer可以将实现接收PosixSignal。它的原理已经在上一节talk_base::PosixSignalHandler中讨论过了,这里就不再多做分析。它的主要函数如下:

 

talk_base::PosixSignalDispatcher::SetHandler:将signal的响应函数加入到分发器

参数说明:

signum:需要响应的signal编号

handler:当signal触发时,响应的处理函数

 

5.6 talk_base::SocketDispatcher

 

talk_base::SocketDispatcher类主要将talk_base::PhysicalSocket封装成一个分发器。所以,在实现上该类仅仅就是为talk_base::PhysicalSocket添加一些talk_base::Dispatcher接口需要的一些成员函数,以及一些状态维护代码。仅有在Linux版本仅有的成员函数talk_base::SocketDispatcher::IsDescriptorClosed中有些比较特殊的情况。通过这个函数中的注释我们发现尚无一些可靠的手段判断一个socket文件描述符是否已经被关闭,所以实现代码使用了::recv(s_, &ch, 1, MSG_PEEK)来判断。在Window版本的talk_base::SocketDispatcher中没有这个成员函数。除此之外,talk_base::SocketDispatcher类的Windows实现和Linux实现基本一致。以下对比一下2个平台API调用的情况:

 

ioctlsocket

fcntl函数用于设置文件描述符的选项(比如阻塞或非阻塞)

 

5.7 talk_base::FileDispatcher

 

talk_base::FileDispatcher类是一个Linux平台独有的类,它的功能是简单将文件描述符封装成Dispatcher。不过它在WebRTC中基本上没有使用过。该类在创建的时候(构造函数中),接受并保存文件描述符,并通过fcntl函数将文件描述符设置为非阻塞。此外它还实现了一些talk_base::Dispatcher接口要求的一些函数,并维护一些状态变量。该类并没有什么难点。

 

5.8 talk_base::Signaler

 

talk_base::Signaler的用处只有一个:实现talk_base::PhysicalSocketServer的signal_wakeup_成员变量(用于解除多路信号分离器的阻塞状态)。它的主要功能由父类talk_base::EventDispatcher实现,仅仅添加了在解除多路信号分离器阻塞状态后将talk_base::PhysicalSocketServer::fwait_成员变量设置为false的代码。talk_base::PhysicalSocketServer::fwait_成员变量在被设置为false后,talk_base::PhysicalSocketServer::Wait函数就会退出,talk_base::MessageQueue就可以及时处理消息队列的消息。
0 0
原创粉丝点击