关于ACE_Asynch_Acceptor::accept 内存泄露的问题(Windows)

本文分析了使用ACE_Proactor和ACE_Asynch_Acceptor时遇到的内存泄露问题,并探讨了解决方案。问题发生在程序正常退出时,尤其是在Windows环境下。

关于ACE_Asynch_Acceptor::accept 内存泄露的问题(Windows)


一、问题描述

使用ACE_Proactor和ACE_Asynch_Acceptor,开发服务器端侦听客户机的连接功能时,当程序正常逻辑退出时,会发生内存泄露的问题,有时会怀疑ACE的BUG,其实不全是如此。

一般情况下,运行ACE_Proactor的线程来自ACE_Task::activate()激活的那些线程。以下是退出时的关键步骤:

	1、调用ACE_Asynch_Acceptor::cancel()

取消当前的侦听工作,这样,就会触发ACE_Proactor框架中的handle_accept事件,携带accept失败的信息给应用程序代码。

	2、调用ACE_OS::closesocket(handle()) 关闭socket句柄,再调用handle(ACE_INVALID_HANDLE)重置变量。

这里为什么要调用关闭socket的逻辑呢。如果这里不关闭,会有一个潜在的问题,就是当有客户端连接后,在正常退出服务器程序,还是会发生同样的内存泄漏问题。关键原因在ACE_Asynch_Acceptor::cancel()里面,使用Windows的CancelIo方法,这个方法要求发起的异步操作和调用CancelIo是同一个线程,如果不是,尽管CancelIo返回true,但是其实没有取消掉listen的socket句柄。然后糟糕的是,完成端口的事件没有发生,handle_accept没有被调用,内存依然没有顺利释放。

因为,一个客户端连接后,处理这个逻辑往往是一个工作线程,比如A线程,当A完成工作后,会再次发起一个异步accept操作。恰恰是这个异步操作是由A线程发起的,而调用CancelIo却在主线程,所以出现上面的情况。

一般情况下,closesocket会在ACE_Asynch_Acceptor的析构函数中调用,但是我们等不到那时,因为我们还是希望由handle_accept()方法被调用,来处理取消或者关闭的错误逻辑。所以这里不合适删除ACE_Asynch_Acceptor对象。

	3、调用ACE_Proactor::proactor_end_event_loop()

让线程忙完当前工作后,回到ACE_Proactor::handle_events的时候,可以顺利退出。如果当前线程阻塞在完成事件的等待中,那么就会发送唤醒线程的指令,让线程们继续。这样所有线程都又回到了handle_events()的地方,也就正常顺利的结束了。

	4、调用ACE_Task::wait()

程序启动时,一般用ACE_Task::activate() 方法来启动一些线程,这些线程配合ACE_Proactor框架来处理事件并分发它们。所以,这里需要调用wait()方法等待这些线程全部正常结束。有了步骤2的工作。这里稍微等一会就返回了,返回时,可以确定一件事情,就是由activate派发出来的线程已经全部顺利、正常的退出了(从svc返回)。

	5、调用ACE_Proactor::close_singleton()

删除ACE_Proactor的单体对象。一般用单体模式,如果是其他,那么删除即可。


基本上,这个逻辑没有大问题,但是如果用内存检测工具检测后发现,有内存泄露,来自这里:

	ACE_Asynch_Acceptor<HANDLER>::accept()方法中的

	// Create a new message block big enough for the addresses and data

	  ACE_NEW_RETURN (message_block,

	                  ACE_Message_Block (space_needed),

	                  -1);

	


二、分析原因

	1、泄露的内存本来是用于AcceptEx异步调用所需的内存,但是程序结束时,上面分配的内存没有全部删除干净。也许可以发现,一共泄露了4块这样的内存,都是这个地方。为什么是4块呢?因为ACE_Asynch_Acceptor<HANDLER>::accept()一共分配了5块,这是默认情况,释放了一块,还有4块没有来得及释放,线程都退出了。
	2、ACE_Asynch_Acceptor<HANDLER>::open()在调用时,会发起5份异步的accept操作。每一份操作都走上面的accept逻辑。open方法有2个参数,就是backlog和number_of_initial_accepts,如果number_of_initial_accepts不指定(默认是-1),那么number_of_initial_accepts最后使用backlog的值,而backlog的默认值是5,所以一共是调用了5次accept()。
	3、这样默认情况下,程序启动后,就有了5个异步accept操作。假如此时让程序正常退出。我们需要的理想情况是调用5次ACE_Asynch_Acceptor::handle_accept()回调。但是往往只有一次。那是因为假如ACE_Task::activate()采用默认调用,那么只有一个线程。handle_accept里面,的确调用了这块内存释放逻辑。问题在于只调用了一次handle_accept()。


三、简单解决方案

原因找到了。接下来就想办法来解决。有两个简单的办法。

	1、在ACE_Asynch_Acceptor::open中,最后一个参数使用1,而不是默认值。这样,就调用一次accept方法,也就是一块内存。
	2、保留5次或者多次accept,假设次数为N。那么这个方案就是:在ACE_Task::activate()中,线程数量>=N,为什么呢?因为当N个accept异步操作发起后,至少有N个线程等待处理他们,如果程序退出时,至少一个线程处理一个操作。正好全部操作的handle_accept得以调用,每一块内存都得到了释放。


四、潜在问题

上面两个解决方案,其实并不是非常强壮和最棒的。比如下面这种情况下,退出程序时,还是可能发生内存泄露。

	在退出瞬间,N个线程中有3个线程在忙一些具体的事情。那么,极有可能出现3份内存泄漏。因为其中只有N-3个accept异步操作得到了handle_accept的调用,有3个可能没有机会,这是因为等这3个线程忙完工作回到handle_events时,发现proactor_end_event_loop已经调用了,所以,线程就退出了,不再处理其他事情。


	另外,如果需要单线程来执行ACE_Proactor框架,同时希望开始时的accept调用还是保留5次的话,这个办法也是行不通的。


五、其他

关于最佳的解决方案,以后再研究分析。


欢迎自由转载!:)

2013年12月19日

<think>嗯,用户提到了一个关于内核配置的警告信息,看起来是关于USBHOST_ASYNCH选项的依赖问题。首先,我需要理解这个警告的具体内容。警告指出USBHOST_ASYNCH被设置为y,但它的直接依赖项USBHOST_HAVE_ASYNCH和USBHOST的值是n,也就是说这些依赖不满足的情况下,它被选中了。然后,用户提到这个选项被HPM_USBOTG在特定的Kconfig文件中选中。 我需要回忆一下Linux内核的Kconfig系统是如何处理配置选项的依赖关系的。每个配置选项可以有依赖关系(depends on),这些依赖必须满足才能选择该选项。此外,如果有其他符号通过select强制启用该选项,但依赖不满足,就会导致冲突,产生这样的警告。 接下来,我需要分析问题出现的原因。HPM_USBOTG可能使用了select来强制启用USBHOST_ASYNCH,但USBHOST_ASYNCH的依赖条件没有满足。这可能是因为HPM_USBOTG的select语句没有考虑USBHOST_ASYNCH的依赖,或者这些依赖本身没有被正确配置。 可能的解决方案有几个方向:首先,检查HPM_USBOTG的Kconfig定义,确保它在select USBHOST_ASYNCH时,同时满足其依赖条件。其次,可能需要调整USBHOST_ASYNCH的依赖,或者添加必要的其他配置选项来满足依赖。另外,也有可能存在配置选项的层级结构问题,需要确保父选项已经正确启用相关依赖。 用户可能需要指导他们如何定位问题,比如查看相关的Kconfig文件,检查依赖关系,或者建议如何修改Kconfig以避免冲突。同时,需要提醒用户在进行这些修改时,要确保理解整个配置的依赖链条,避免引入新的问题。 另外,考虑到用户可能是开发者或系统管理员,他们可能需要具体的步骤来修改Kconfig文件,例如在HPM_USBOTG的select语句后添加依赖条件,或者在USBHOST_ASYNCH的定义中调整依赖。需要确保语法正确,例如使用depends on和select的正确组合,或者使用imply代替select,如果适用的话。 最后,可能需要验证修改后的配置是否解决了警告,并测试相关功能是否正常工作,确保没有遗漏其他依赖项。</think>这个警告信息是Linux内核Kconfig配置系统中常见的依赖关系冲突提示,我来为你逐步分析: 1. **基本概念**: - `USBHOST_ASYNCH` 是一个Kconfig配置选项 - 它的定义位置在`drivers/usbhost/Kconfig:45` - 该选项存在直接依赖关系:`USBHOST_HAVE_ASYNCH && USBHOST` 2. **当前冲突**: - 直接依赖条件的结果是`n`(不满足) - 但该选项被强制设置为`y`(使能) - 强制使能它的符号是`HPM_USBOTG`(定义在`arch/risc-v/src/hpmicro/Kconfig:1565`) 3. **技术原理**: - Kconfig系统中,`select`语句可以强制使能某个选项 - 但被`select`的选项必须满足其`depends on`条件 - 此处`HPM_USBOTG`通过`select USBHOST_ASYNCH`强制使能了该选项,但未满足其依赖条件 4. **问题定位**: - 检查`HPM_USBOTG`的Kconfig定义: ``` config HPM_USBOTG ... select USBHOST_ASYNCH # 问题根源在此 ``` - 检查`USBHOST_ASYNCH`的依赖: ``` config USBHOST_ASYNCH depends on USBHOST_HAVE_ASYNCH && USBHOST # 这两个条件必须同时满足 ``` 5. **解决方案**: - **方案1**:修正`HPM_USBOTG`的依赖链 ```kconfig config HPM_USBOTG depends on USBHOST_HAVE_ASYNCH && USBHOST # 添加相同依赖 select USBHOST_ASYNCH ``` - **方案2**:调整`USBHOST_ASYNCH`的依赖条件(需评估影响) ```kconfig config USBHOST_ASYNCH depends on USBHOST # 移除USBHOST_HAVE_ASYNCH依赖 ``` - **方案3**:确保依赖条件被满足 ```kconfig config USBHOST_HAVE_ASYNCH def_bool y # 强制设置为y config USBHOST def_bool y # 强制设置为y ``` 6. **验证方法**: ```bash # 检查相关配置的最终状态 make menuconfig # 交互式查看 make nconfig # 或使用ncurses界面 ``` 7. **设计建议**: - 避免在Kconfig中使用`select`强制使能具有复杂依赖的选项 - 推荐使用`imply`代替`select`,保留用户的最终控制权 - 保持依赖链的完整性,遵循"依赖传播"原则 这个警告表明当前配置存在逻辑矛盾,虽然编译可以继续,但可能导致运行时异常。建议优先采用方案1进行修正,确保依赖关系的正确传递。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值