您当前位置: 首页 »

win api

分类目录归档: win api

【任务处理模型、性能相关】windows上窗口循环、单一线程循环、iocp线程任务处理模型比较

下面图分别简易说明三种较常用的任务处理线程模型。

1

2

3

 

 

 

先说说为什么会来比较这三种任务线程模型,主要还得来于Chromium和手头工作中实际运用的特定问题;这三种任务线程模型在Chromium base的message_loop中都有过实现,且均用在不同的模块中(尽管message_loop中有大部分代码和boost中很像,或者就说直接拷过来的)。不过还是有必要值得讨论下这三种任务线程模型的特点。

 

  • 单一线程循环:

这种模型构造最为简单,除了循环线程外,一般由一个临界、一个事件、和一个可以存放任务的数组或者stl容器组成。

但只可以同一进程内使用,或者经过内存映射后的跨进程通讯(一般没人这么干)。

任何任务的投递均要经历:

1,获取临界

2,往任务池中投递任务

3,激活事件

 

循环线程一般流程:

1,获取临界

2,取出任务池中的任务/等待事件激活

3,处理任务

 

对于循环线程可能会产生态变迁位置:

1,获取临界(虽然临界是用户态对象,但有可能会导致线程挂起等) —>  用户态转内核态

2,等待事件  —>  用户态转内核态

3,事件被激活,线程准备恢复执行    —>  内核态转用户态

 

总结:简单,态切换较少,且可能存在较大的线程时间处于用户态。速度快

 

  • 窗口消息:

*对窗口消息了解不是很多,所以这里只说一下仅有的少量了解。

这种模型构造组成:窗口、消息循环线程、其他辅助(例如要传递一个大内存,可能会用到共享内存等)。

特点:了解windows消息机制就可以用,且可以方便的处理一些和windows消息有关的任务,利用窗口消息分发特点可以很容易的划分任务类型。跨进程。

 

任何任务的投递均要经历:

1,获取窗口真实句柄

2,投递消息给窗口

 

循环线程一般流程:

1,等消息

2,取消息

3,分发消息

对于循环线程可能会产生态变迁位置:

1,等消息  —>   用户态转内核态

2,取消息(可能会出现,因为消息队列是用共享内存实现的,不清楚要不要做加锁操作)     ->   用户态转内核态

3,分发消息(可能,由于一些消息是默认windows消息,或者是消息钩子链上的消息,需要把消息丢回消息链上,让其他进程进行处理)    —>  用户态转内核态

 

  • iocp:

*对iocp了解不是很多,所以这里只说一下仅有的少量了解。

这种模型构造组成:iocp模型、循环线程、其他辅助。

特点:可以方便的处理io相关的任务,内核态会创建一定量的线程数与用户态对应,需要做多次软中断;可跨进程。

任何任务的投递均要经历:

1,获取完成端口真实句柄

2,投递完成端口事件

循环线程一般流程:

1,等事件

2,取事件

3,处理事件

对于iocp线程可能会产生态变迁位置(实际上不全,因为在内核态还需要进行一次APC级别的软中断,iocp这种模型的一个任务流转过程,中断很频繁。详情请参考深入理解windows):

1,等事件  —>   用户态转内核态

2,取事件    ->   用户态转内核态

3,处理事件

由于手头有份代码,当初的作者可能为了简单或者什么原因,借鉴了boost的iocp模型,并形成一个单线程的底层驱动模型。

早在一年前,刚好要在这个模型上面添加一个及时性要求比较的功能时,发现任务处理的及时性不稳定。有时候任务处理很及时,有时候任务处理延时很重。每个任务的消耗都尽量保持在一个相对平均的水平。

在该模型中频繁堆叠较重的任务后,发现原来是该模型任务处理性能不稳定(被测电脑也是个性能较差的电脑)。后来将及时性任务从该框架中剥离出来以后,问题得到了较好的缓解。

最近在boost,刚好手里有代码,就做个简单的测试。简易代码在这

QQ图片20160517113906

QQ图片20160517113920

其中77s对应的图是release下iocp模型,空转1亿次的耗时

其中46s对应的图是release下简单线程模型,空转1亿次的耗时

可见如果只是比较简易线程和iocp模型的话,简易线程的性能会比iocp快不少,但由于特定使用情况,也不能过分的教条。

2016-05-17 | | win, win api, 思考, 编码技巧

【任务处理模型、性能相关】windows上窗口循环、单一线程循环、iocp线程任务处理模型比较已关闭评论

【win8.x & vs 2013】windows 8.1之后可能出现的api差异

今天把工程全部转换成了vs 2013,主要是通过vs2013下的一些动静态代码检查功能。转换成vs 2013以后,发现编译器的检查更加严格了。一些警告会被视为异常,例如:

OSVERSIONINFO sInfo;
sInfo.dwOSVersionInfoSize = sizeof(sInfo);
GetVersionEx(&sInfo);

这段代码在vs 2013默认语法检查规则中是编译无法通过的。
查看msdn对GetVersionEx的说明(https://msdn.microsoft.com/zh-cn/library/ms724451(v=vs.85).aspx)

[GetVersionEx may be altered or unavailable for releases after Windows 8.1. Instead, use the Version Helper APIs]

该api由于不安全,在windows 8.1中会有一组函数进行对其替代。同时,对stl和编译预处理的检查将开始更为严格。后续需要对windows 8.1的api和之前windows的api进行差异性了解。通过可得知vs 2013的设计就是为了操作系统而准备的,有意或无意的改变程序员的部分编程习惯。

2015-12-29 | | win, win api, 编码技巧

【win8.x & vs 2013】windows 8.1之后可能出现的api差异已关闭评论

【windows】关于频繁创建和关闭进线程开销大的原因理解之一(1)

首先从进程角度去看,内部其实是由一个或者若干个线程组成。因此在理解进程频繁创建和关闭导致的开销问题,只需要理解一个单一线程的频繁创建和关闭即可。

CreateThread、TerminateThread

这两个函数一个用于创建线程,另一个用于关闭线程。

1,先说一下线程创建的过程。对于CreateThread来说,创建的均为用户态线程。尽管创建的是用户态线程,但对于函数被调用者来说,本身还是需要切换到内核态或等待内核态服务线程将线程创建起来。

仅从这一点去看,有了态切换。显然就存在各种开销。

2,在内核创建好线程以后,这个线程将会被纳入windows的线程调度中,同时该线程所占有的内存资源也会被纳入内存调度中。仅从这里去看,又可以看到两个调度器的任务增加,且内存资源在减小。

3,在线程退出后,内核需要对其内存进行回收。在回收过程中,需要对线程的堆栈等系统分配给的内存资源进行校验(退出时的crash生成也在这一步)。检查完成以后,又要将回收回来的内存插回到windows的堆栈管理器里面(不管是crt堆,还是win32堆或者系统堆,都要插回去)。在回插内存的时候,可能还会引发堆内存的合并和子堆往父堆进行归还的过程。

 

通过以上描述,基本可得知线程的创建和关闭的开销是相当大的事情。

如果再深入一点看的话,可能会更清楚一些。

由于在windows系统以后,系统会“虚拟”出一个system进程,该进程内部其实是一堆辅助线程(有时候创建IO句柄时,也会出现这个进程里面的线程数增加,或者做调试的时候也有可能会出现类似的现象)。

system里面的线程,主要是用于服务在不同IRQL和内核用户态之间。换句话说,相当于一个桥(个人猜测,不负责哈!)。

当一个线程要进行关闭时,该system里面的辅助线程(15级以上),会对线程回收工作进行处理。换句话说,如果一个线程自己是正常退出的,那么自己会抛一个系统请求给system,让system把自己的内存回收掉。这个过程又要走一堆windows服务程序(服务端口),从这一点上看可得知。

一个线程的关闭,势必会激活一个“紧急”任务的进行,且会涉及到一个较长的流程。应此无论是创建还是关闭线程,如果执行频度太过于高,显然是不行的。

2015-12-15 | | win, win api, 编码技巧

【windows】关于频繁创建和关闭进线程开销大的原因理解之一(1)已关闭评论

通过windows和linux在socket上的几点相同点,来揣测windows和linux在某些实现上的相似

首先,我是一个完全的linux开发初学者,我仅仅在windows做开发,由于兴趣与工作的缘故碰到了通过已有的socket模型搭建好的网络库。下面就说说在windows下,我了解到socket模型(准确是标准socket模型,对于微软封装过的一些模型我不是很清楚)

1,普通阻塞是socket:

普通的socket模型,就是msdn中对socket api族介绍中最简单的那个例子。这种最简单,在执行效率上相对较低,在处理send/recv(sendto/recvfrom)时,函数是处于阻塞的。在简单环境,或者写一些简单的测试工具时,这是一种比较理想的用法。

2,同步 select模型

同步select模型,其实可以是理解为普通阻塞式sokcet的一个扩展,就是利用select来对socket句柄进行监听,避免由于调用send/recv(sendto/recvfrom)时出现阻塞。

3,异步select模型

异步select模型,可以看成是同步select的一种变化。即send/recv(sendto/recvfrom)函数不会阻塞,调用立即返回,并需要同时配合select函数一同使用。

4,IOCP

异步IO模型,使用上可以和异步select模型类比;但这并非标准socket模型。

 

然后接下来再说一下几个模型的差别:

1,普通socket、select模型,在调用send/recv(sendto/recvfrom)函数时,均会出现一次多余的内存拷贝。即,用户态传如一个buffer时,在转为交给协议栈之前,必须通过内存拷贝的方式,将数据复制到内核态的内存中。

2,windows上select集合最大值大约是64/128(有系统版本决定);换句话说一个调用select函数的线程,最大可监听64/128个socket句柄

*3,据网上各种文档介绍到,select是基于内核态线程轮询完成,具体不详,有待考证!

4,IOCP 和其他几者最大区别在于内存拷贝上,由于IOCP是通过共享内存(即内存映射)。在windows中内存映射属于一个特殊的内存管理器的管辖范围,他即不属于内核态,也不属于用户态内存,当对于内核态或用户态线程来说,只要通过调用内存映射函数,就可以直接访问这块内存(但得到的逻辑地址或许是不同的)。

由于在WSARecv/WSASend之类函数中,提交的均为共享内存,因此无需做任何内存拷贝。所以在IOCP上,性能要更好的多。同时本人的了解范畴也仅限于这一块,对于IOCP的事件投递过程并不是很清楚,但根据《软件调试》一书中对“windows系统调试/windows软件调试”设计的讲述来看,IOCP这种的实现应该是在内核态也同样创建了和用户态以之对应的内核服务线程。

 

接着再说说linux,刚刚看了一下epoll的介绍。

发现epoll是基于事件回调+内存映射来实现IO上的高效;同时这篇文章也还介绍了几个常用的socket模型在linux中的大概实现。通过这篇文章至少能够看出,linux和windows在对socket的实现上存在很大的相似。

可见linux和windows存在很多相似的地方,如果linux和windows互相对应着学,理解起来可能会快很多。

2015-11-16 | | [linux总结], linux kernel, unix编程环境学习, win api

通过windows和linux在socket上的几点相同点,来揣测windows和linux在某些实现上的相似已关闭评论

关于windows是一个非实时系统的验证和讨论

再看《深入理解windows》的时候,书中提到windows并非总是一个实时系统。主要原因是由于系统本身的设计,以及驱动,应用软件各方面一同构成的;然后就偶然了解到RTX这个辅助工具。

看了一下RTX的介绍,实际上RTX就是一个和windows内核相仿的一个系统。然后RTX会工作在一个或者几个独立的vcpu/cpu上,使得该vcpu/cpu变成一个实时系统。进而进行部分接管windows的一些中断响应、驱动操作。

然后就看到这个RTX的关于如何配置的视频时,听到对方介绍到用到RTX和没有用到RTX的系统。在用到了RTX的windows里面,对于一个1us采样的5s beep音频来说,能够流畅的播放出来。而对于一个没有使用RTX的是系统来说,就会出现断断续续的问题。

接着因为感兴趣对方是如何测试,就随手写了一段代码:


#include "stdafx.h"
#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
for (int i =0; i<5000; ++i)
{
DWORD dwstart = GetTickCount();
Beep(750, 60);
DWORD dwend = GetTickCount();
printf("now cur %d, cost time %d\n", i, dwend - dwstart);
}

Sleep(1000*3600);
return 0;
}

 

由于gettickcount本身也是个严重不准的时间函数,所以也只能间接看一看;且整个声音均为断断续续。

通过测试发现,每个循环周期都有可能不是稳定的,有时候会存在2-5ms的误差,也或许和gettickcount的分辨率有关,或许确实和beep函数有关。

 

后来通过代码修改为如下,整个音频则变为流畅:


#include "stdafx.h"
#include &lt;Windows.h&gt;

int _tmain(int argc, _TCHAR* argv[])
{
Beep(750, 5000);
Sleep(1000*3600);
return 0;
}

通过上述观察然后跟踪了一下beep的汇编,汇编代码如下:
无标题

无标题

在msdn在NtDeviceIoControlFile中的描述:
无标题

参考beep函数的说明以后,在通过上述的描述;可见每调用一次beep始终是等待硬件处理完成以后,才会返回beep。然后又因为windows为非实时系统,所以就会出现上诉的代码1会出现发声会出现断断续续的问题。因为各种中断什么时候能够被处理,或者处理结束都是没有很强的约束性的。

当然,sleepex本身也是一个要走软中断的模块,显然也会影响到代码的发声情况。至于sleepex的精度情况,我自己没找到msdn官方说明,但参考waitforsingleobject和sleep,那sleepex精度也不会很高。

2015-10-28 | | win, win api, 编码技巧, 音视频_图像相关, 音视频编解码

关于windows是一个非实时系统的验证和讨论已关闭评论

DebugBreak 与 _CrtDbgBreak 区别

首先_CrtDbgBreak是的 断言函数。

DebugBreak 与 _CrtDbgBreak 都是断言函数。

 

根据msdn中对DebugBreak的描述 和 (DebugBreak vs AfxDebugBreak vs _CrtDebugBreak vs __debugbreak)得知:

DebugBreak:

1,在release和debug版本下均有效

2,是一个系统调用,在做调试的时候,需要把windows的符号表加载起来一起用才可以观察到完整的异常栈,否则可能只看得到最后的那一点(帧)栈信息

_CrtDbgBreak

1,只有在debug下有效

2,函数可能在msdn上没有任何说明,类似私有api?或未写入文档的官僚函数(M$一贯的作风)?

 

其中在msdn的文档中提到,和DebugBreak相似的函数还有__debugbreak而,__debugbreak是不需要符号表,具体的 __debugbreak文档 并没有找到

 

在VS中创建一个win32工程,当使用VS/win自带的类库出现异常时,在debug版本下的断言均是_CrtDbgBreak被调用。

2014-10-23 | | win api

DebugBreak 与 _CrtDbgBreak 区别已关闭评论

[WIN] CreateProcess 和 ShellExecute 的关键区别

在windows shell program中讲到。

ShellExecute:
1,ShellExecute是对CreateProcess进行封装的。
2,ShellExecute支持直接打开一个文档(例如a.doc)。主要是由于在注册表的ROOT项下,有一个正对Shell/Open/Command的default键有一个关联属性在里面,因此可以直接打开一个文档。只需要传入对应的动作
3,在做相应的启动进程操作时, ShellExecute会对操作系统的策略做检查,对于策略中不允许启动的进程,那就不会启动。

CreateProcess :
1,对策略不做校验
2,只能打开进程,不能打开文档
2013-03-01 | | win api

[WIN] CreateProcess 和 ShellExecute 的关键区别已关闭评论