我有一个Android本机共享库(.so),用于Android应用程序.该库最初是为windows/desktop编写的,然后移植到移动平台.它包含一个"算法"代码,适用于大型数据集.在库中我不使用标准堆(malloc和朋友),而是mmap使用标志分配内存页MAP_PRIVATE | MAP_ANONYMOUS,然后适当地进行分区.
现在,问题是在某些时候mmap失败,错误代码= 12,这是内存不足.当分配的总内存大小达到大约650MB时,会发生这种情况.我知道这个值非常大,远远高于典型的Android应用程序所需要的.但在我的具体情况下,这是合法的恕我直言,因为这确实是这个库/应用程序所做的,并且这是由用户实现和批准的.
具体来说,我尝试在三星平板电脑SM-T800上运行它,它具有3GB RAM,32位armeabi-v7a架构和超过7GB的免费存储空间(闪存).所以在技术上应该没有问题.
这也不是虚拟内存碎片的问题:mmap当我要求分配多达16MB的额外内存块时失败.因此,很可能系统会对可以为进程分配多少内存页面施加一些人为限制.
所以,我的问题是,是否以及如何消除此限制.从我在在线文档中发现的,没有提到这个限制,但我很确定它存在.我还在一些论坛中读到,从Android 5.0开始,一些应用程序无法分配尽可能多的内存,而不是旧系统.
如果无法删除此限制,是否有助于处理文件映射?目前我以压缩方式将数据存储在源文件中,然后我读取它并在内存中构建复杂的数据结构.相反,我可以将整个数据结构存储在一个文件中(意思是,文件会更大),然后通过它将它映射到内存中mmap.虚拟地址空间的总大小将是相同的,但如果限制不是它的大小,而是分配了多少页面而不是文件支持 - 这可能有效.
作为最后的手段,我可以放弃在(虚拟)内存中存储所有数据的想法,并手动读取和锁定我当前需要的数据部分,并丢弃最近未使用的数据部分.但是通过这种方式我实际上复制了内存管理器的工作,因为分页机制就是这样做的.
提前致谢.
对不起这么长的问题.我只是花了好几天试图解决我的问题,而且我已经筋疲力尽了.
我正在尝试以异步模式使用WinINet.我必须说......这简直就是疯了.我真的无法理解这一点.它做了很多事情,但遗憾的是它的异步API设计得非常糟糕,以至于它不能用于具有高稳定性要求的严肃应用程序.
我的问题如下:我需要连续进行大量的HTTP/HTTPS事务,而我还需要能够在请求时立即中止它们.
我将按照以下方式使用WinINet:
InternetOpen带有INTERNET_FLAG_ASYNC标志的函数初始化WInINet用法.InternetSetStatusCallback).现在,为了执行我想做的事务:
InternetOpenUrl发起交易.在异步模式下,它通常会立即返回错误,即ERROR_IO_PENDING.其中一个参数是'context',它将传递给回调函数.我们将其设置为指向每个事务状态结构的指针.INTERNET_STATUS_HANDLE_CREATED.此时我们保存WinINet会话句柄.INTERNET_STATUS_REQUEST_COMPLETE在事务完成时调用回调函数.这允许我们使用一些通知机制(例如设置事件)来通知原始线程事务已完成.InternetCloseHandle),并删除状态结构.到目前为止似乎没有问题.
如何中止正在执行的事务?一种方法是关闭相应的WinINet句柄.而且由于WinINet没有这样的功能InternetAbortXXXX- 关闭句柄似乎是唯一的中止方式.
确实这很有效.这样的事务立即完成ERROR_INTERNET_OPERATION_CANCELLED错误代码.但是这里所有的问题都开始了......
我所遇到的第一个令人不快的意外的是,往往的WinINet有时调用回调函数,即使是交易后,它已被中止.根据MSDN,它INTERNET_STATUS_HANDLE_CLOSING是回调函数的最后一次调用.但这是谎言.我看到的是,有时会有INTERNET_STATUS_REQUEST_COMPLETE相同句柄的后续通知.
我还尝试在关闭之前禁用事务句柄的回调函数,但这没有帮助.似乎WinINet的回调调用机制是异步的.因此 - 它甚至可以在事务句柄关闭后调用回调函数.
这就产生了一个问题:只要WinINet 可以调用回调函数 - 显然我无法释放事务状态结构.但是我怎么知道WinINet是否会这么称呼它呢?从我看到的 - 没有一致性.
不过我已经解决了这个问题.相反,我现在保留一个分配的事务结构的全局映射(当然是受关键部分保护).然后,在回调函数内部,我确保事务确实存在并在回调调用期间锁定它.
但后来我发现了另一个问题,到目前为止我无法解决.当我在交易开始后不久中止交易时就会出现这种情况.
发生的是我调用InternetOpenUrl,它返回ERROR_IO_PENDING错误代码.然后我只是等待(通常很短),直到INTERNET_STATUS_HANDLE_CREATED通知调用回调函数.然后 - 保存事务句柄,现在我们有机会在没有句柄/资源泄漏的情况下中止,我们可以继续.
在这一刻之后,我试图完全中止.也就是说,收到后立即关闭此手柄.猜猜会发生什么?WinINet崩溃,内存访问无效!这与我在回调函数中做的任何事情无关.甚至没有调用回调函数,崩溃在WinINet内部的某个地方.
另一方面,如果我等待下一个通知(例如'解析名称') - 通常它会起作用.但有时崩溃也是如此!如果我Sleep在获得句柄和关闭它之间放一些最小的问题似乎消失了.但显然这不是一个严肃的解决方案. …
我的问题:
当 win32k.sys 加载到会话空间时,它在每个会话中是否获得相同的基地址?
细节:
我正在为 Windows(32 位)编写内核模式设备驱动程序。它在系统引导期间作为标准 WDM 驱动程序加载到系统空间(全局内核模式内存)中。
然而,在某些情况下,我需要访问 win32k.sys 导出的函数。确切地说,我正在编写一种有时需要伪装成显示驱动程序的驱动程序。
我可能不会静态导入这些函数(意味着通过可执行导入表导入它们)。这是因为win32k.sys是在会话创建的后期阶段加载的。而且,它被加载到会话空间中。
尽管如此,我还是找到了解决方法。在会话创建期间,我动态导入所需的函数。我使用ZwQuerySystemInformationwith来查找当前会话中SystemModuleInformationwin32k.sys 的基地址。然后使用这个基地址对其进行分析以找到win32k.sys的导出目录并获取所需的函数指针。
目前,对于每个会话,我都会保留一组单独的导入函数。然而实际上,这些功能在所有会话中始终相同。意味着 - win32k.sys 被映射到属于每个会话中的会话空间的相同地址。
因此,我的问题是,是否可以保证 win32k.sys 将在所有会话中映射到相同的地址?
除了节省一些内存之外,这会让我的事情变得更容易。目前,为了调用这样的函数,我需要一个存储函数指针的特定于会话的上下文。
我需要找到连接两个平面点的最佳路径.我给出了一个确定最大前进速度的函数,它取决于位置和时间.
我的解决方案基于Dijkstra算法.首先,我通过2D晶格覆盖平面,仅考虑离散点.点与指定顺序的邻居相连,以获得足够的方向分辨率.然后我找到了(有点)Dijkstra算法的最佳路径.接下来,我提高了找到的路径的分辨率/质量.我增加了网格密度和邻居连接顺序,同时将搜索仅限制在足够接近已找到路径的点上.这可以重复,直到达到所需的分辨率.
这通常很好,但是我想提高整体算法性能.我已经基于价格函数的"平滑度"实现了几个技巧,例如可变网格密度和邻居连接顺序.但是我相信有可能改进Dijkstra算法本身(适用于我的特定图形),我还没有完全意识到.
首先让我们就术语达成一致.我将所有格点分为3类:
在每一步,Dijkstra算法选择"最便宜"的暖格点,然后尝试提高其邻居的价格.由于我的图形的性质,我得到了一种稳定点的云,周围是一层薄薄的暖点.在每个步骤中的温暖在浊点周长被处理,那么它加入到稳定云,并且暖周长是(潜在的)展开.
的问题是,暖被随后通过算法处理点通常是在空间上(因此-拓扑)无关.典型的温暖周界由数十万个点组成.在每一步的下一个暖点的过程是伪randomal(空间),因此实际上有没有机会,两个相关点被处理了一个又一个.
这确实会产生CPU缓存利用率的问题.在每一步,CPU处理伪随机存储器位置.由于存在大量的热点 - 所有相关数据可能都不适合CPU缓存(它的数量级为几十到几百MB).
嗯,这确实是Dijkstra算法的含义.无论其他属性如何,整个想法都明确地选择最便宜的热点.
但直观地说,很明显,大云外围一侧的点对另一侧的点(在我们的具体情况下)没有任何意义,并且交换其处理顺序没有问题.
因此,我考虑了"调整" 暖点处理顺序的方法,但一般不会影响算法.我想到了几个想法,比如将飞机划分为块,并且在满足某些标准之前将其部分解决,这意味着他们的解决方案可能会受到干扰.或者可选地忽略干扰,并且可能允许"重新解决"(即从稳定回到温暖).
但到目前为止,我找不到严谨的方法.
有什么想法怎么做?也许这是一个已知的问题,现有的研究和(希望)解决方案?
提前致谢.抱歉这个长期的问题.
一旦我为Windows编写了一种驱动程序,它必须拦截本机显示驱动程序与操作系统的交互.本机显示驱动程序由一个微型端口驱动程序和一个由win32k.sys加载到会话空间的DLL组成.我的目标是介入win32k.sys和该DLL之间.此外,系统可能有几个显示驱动程序,我不得不挂钩它们.
我创建了一个标准的WDM驱动程序,它被配置为在系统启动时加载(即在win32k之前).在初始化期间,它ZwSetSystemInformation通过修补SSDT来连接.每当它将DLL加载/卸载到会话空间时,OS就会调用此函数,这正是我所需要的.
何时ZwSetSystemInformation使用SystemLoadImage参数调用- 其中一个参数是指向SYSTEM_LOAD_IMAGE结构的指针,它ModuleBase是模块基本映射地址.然后我分析映射的图像,用我的函数修补它的入口点,其余的很简单.
现在我需要将此驱动程序移植到64位Windows.毋庸置疑,这根本不是一项微不足道的任务.到目前为止,我发现了以下障碍:
如果我理解正确,可以关闭PatchGuard和驱动程序签名验证,驱动程序应该安装在专用机器上,我们可能会按照我们想要的方式折磨它.
据在线消息人士透露,还有一些技巧可以找到SSDT.
但是最近我发现存在一个叫做的函数PsSetLoadImageNotifyRoutine.它可以大大简化任务,并帮助避免肮脏的技巧.
我的问题是:
PsSetLoadImageNotifyRoutine,我会收到有关加载到会话空间的DLL的通知吗?官方文档谈到"系统空间或用户空间",但"系统空间"是否还包括会话空间?提前致谢.
根据WinAPI可执行文件可能包含资源部分.常见的资源类型之一是版本info(VERSIONINFO).它由一个固定的部分组成,加上它可以包含任意数量的字符串条目(名称+值对).
我的构建环境是这样安排的,每当我构建一个可执行文件时 - 它的版本信息会自动调整以反映源代码控制的当前状态.因此它始终包含其构建信息.
直到上个月,当我还在使用旧的XP机器时,我可以右击任何这样的可执行文件,选择属性/版本,然后 - 瞧!我可以看到所有构建信息.但最近我将我的机器升级到Windows 7.而且,如发现的那样,它不再显示丰富的版本信息.它仅显示固定版本信息部分.
有谁知道这是否可以修复?也许在某个地方存在一个选项来启用以前的版本信息选项卡?
PS丰富的版本信息是有,它不是积累的问题.当我用资源编辑器打开文件时,我可以看到这一点.
提前致谢.
UPD:
我的意思是"丰富的版本信息".
http://msdn.microsoft.com/en-us/library/ms647001%28v=vs.85%29.aspx
据此,版本信息包含强制部分(包括产品和文件信息).另外,它可能包含任意字符串对.有关StringFileInfo更多信息,请参阅
在我的可执行文件的版本信息中,我使用这些自定义字符串来提供丰富的源代码控制信息(我使用SVN).
修订号进入文件/产品次要版本号,这里没有问题.但是我也放了一些自定义字符串,如下所示:
等等.
Windows XP UI在shell的标准版本信息选项卡中显示了所有这些字符串.他们刚刚在Windows 7中消失了.
问题是如何把它们放回去.
我需要一种方法来在编译期间验证指向另一个类(派生或基类)的指针的向上/向下转换不会改变指针值.也就是说,演员阵容相当于reinterpret_cast.
具体来说,场景如下:我有一个Base类和一个Derived类(显然是派生自的Base).还有一个模板Wrapper类,它由指向作为模板参数指定的类的指针组成.
class Base
{
// ...
};
class Derived
:public Base
{
// ...
};
template <class T>
class Wrapper
{
T* m_pObj;
// ...
};
Run Code Online (Sandbox Code Playgroud)
在某些情况下,我有一个类型的变量Wrapper<Derived>,我想调用一个接收(const)引用ro的函数Wrapper<Base>.显然这里没有自动投射,Wrapper<Derived>不是源于Wrapper<Base>.
void SomeFunc(const Wrapper<Base>&);
Wrapper<Derived> myWrapper;
// ...
SomeFunc(myWrapper); // compilation error here
Run Code Online (Sandbox Code Playgroud)
有一些方法可以在标准C++的范围内处理这种情况.像这样例如:
Derived* pDerived = myWrapper.Detach();
Wrapper<Base> myBaseWrapper;
myBaseWrapper.Attach(pDerived);
SomeFunc(myBaseWrapper);
myBaseWrapper.Detach();
myWrapper.Attach(pDerived);
Run Code Online (Sandbox Code Playgroud)
但我不喜欢这个.这不仅需要一个笨拙的语法,而且它还会产生一个额外的代码,因为它Wrapper有一个非平凡的代码(你可能已经猜到了),而且我正在使用异常处理.OTOH如果指针指向Base和Derived相同(就像在这个例子中,因为没有多重继承) - 一个人可能只是 …
在一次采访中,我被要求(除其他事项外)实施以下功能:
int StrPrintF(char **psz, const char *szFmt, ...);
Run Code Online (Sandbox Code Playgroud)
类似于sprintf,除了已经分配的存储,函数必须自己分配它,并返回*psz变量.此外,*psz可能指向已经分配的字符串(在堆上),这可能在格式化期间使用.当然,这个字符串必须通过适当的方式释放.
返回值应该是新创建的字符串的长度,或者是错误时的负数.
这是我的实施:
int StrPrintF(char **psz, const char *szFmt, ...)
{
va_list args;
int nLen;
va_start(args, szFmt);
if ((nLen = vsnprintf(NULL, 0, szFmt, args)) >= 0)
{
char *szRes = (char*) malloc(nLen + 1);
if (szRes)
if (vsnprintf(szRes, nLen + 1, szFmt, args) == nLen)
{
free(*psz);
*psz = szRes;
}
else
{
free(szRes);
nLen = -1;
}
else
nLen = -1;
}
va_end(args);
return nLen; …Run Code Online (Sandbox Code Playgroud) 我们在生产中有一个代码,在某些情况下,它可能会将 32 位无符号整数左移超过 31 位。我知道这被认为是未定义的行为。不幸的是,我们现在无法解决这个问题,但我们可以解决这个问题,前提是我们可以假设它在实践中是如何工作的。
在 x86/amd64 上,我知道用于移位的处理器仅使用移位计数操作数的适当较低有效位。所以这a << b实际上相当于a << (b & 31). 从硬件设计来看,这是完全合理的。
我的问题是:这在现代流行的平台(例如 arm、mips、RISC 等)上如何在实践中工作。我的意思是在现代 PC 和移动设备中实际使用的那些,而不是过时或深奥的。
我们可以假设它们的行为方式相同吗?
编辑:
我正在谈论的代码目前在区块链中运行。它究竟如何工作并不重要,但至少我们希望确保它在所有机器上产生相同的结果。这是最重要的,否则可以利用它来诱导所谓的链分裂。
修复这意味着麻烦,因为修复应该同时应用于所有正在运行的机器,否则我们将再次面临链分裂的风险。但我们会在某个时候以有组织(受控)的方式来做这件事。
各种编译器的问题较小。我们只使用 GCC。我亲眼看了一下代码,里面有shl说明。坦率地说,鉴于上下文,我不希望它有任何不同(移位操作数来自任意来源,无法在编译时预测)。
请不要提醒我“不能假设”。我知道这个。我的问题是 100% 实用的。正如我所说,我知道在 x86/amd64 上,32 位移位指令仅占用位计数操作数的 5 个最低有效位。
这在当前的现代架构中表现如何?我们还可以将问题限制在 little-endian 处理器上。
我说的是物理磁盘驱动器,而不是卷/分区/逻辑驱动器.所以通常建议的GetVolumeInformation功能在我的情况下不适用.
确切地说:我正在直接处理尚未分区的磁盘.我通过CreateFile函数打开它的句柄:
hDisk = CreateFile(
_T("\\\\.\\PHYSICALDRIVE0"),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING,
NULL);
Run Code Online (Sandbox Code Playgroud)
我可以使用这个句柄直接在磁盘上读/写.还可以使用DeviceIoControl函数查询各种磁盘属性.但是,我找不到查询磁盘供应商/串行属性的方法,这些属性在设备管理器中可见.
我正在将(由我)为 Windows 编写的项目移植到移动平台。
我需要一个相当于VirtualAlloc(+friends) 的,自然是mmap. 但是,有 2 个显着差异。
返回的地址VirtualAlloc保证是所谓的分配粒度( dwAllocationGranularity) 的倍数。不要与页面大小混淆,这个数字是任意的,在大多数 Windows 系统上是 64K。相比之下,返回的地址mmap只能保证页面对齐。
可以通过调用 立即释放保留/分配的区域VirtualFree,并且不需要传递分配大小(即 中使用的大小VirtualAlloc)。相反,munmap应该给出要取消映射的确切区域大小,即它释放给定数量的内存页,而与它们如何分配没有任何关系。
这给我带来了问题。虽然我可以忍受 (2),但 (1) 是一个真正的问题。我不想深入细节,但假设更小的分配粒度,例如 4K,将导致严重的效率下降。这与我的代码需要在分配区域内的每个粒度边界处放置一些信息的事实有关,这会在连续内存区域内施加“间隙”。
我需要解决这个问题。我可以考虑分配增加的区域的非常幼稚的方法,以便它们可以 64K 对齐并且仍然具有足够的大小。或者,保留巨大的虚拟地址空间区域,然后分配正确对齐的内存区域(即实现一种对齐的堆)。但我想知道是否有其他选择。例如特殊的 API,可能是一些标志、秘密系统调用或其他什么。