很多时候程序的 Debug 版本运行没有任何问题,但是一旦发布 Release 版本后,运行就出错,着实让人郁闷。大家知道,VC++ 中 Release 版本是对无法对源代码进行调试的。一般的做法是在怀疑有错误的代码前后插入MessageBox 函数,在函数中显示可能导致错误的变量的值。或者插入写文件的语句,输出可能导致错误的变量的值到一个记录文件。其实,除了上面讲的这个办法之外,还有其它的途径来调试 Release 版本的。下面就结合自己的经验和网上查找的一些资料给出调试 Release 版本的两个方法:方法一、利用 *.PDB 符号文件调试 Release 版本在 VCKBASE 的在线杂志中有一篇参考文章:符号文件——Windows 应用程序调试必备(http://www.vckbase.com/document/viewdoc/?id=1710),文章谈到了如何产生 Release 版本二进制文件对应的 PDB 文件的问题。有了 PDB 文件后,就可以调试 Release 了,方法是:1、在Project Settings里选Settings For为All Configurations。 2、在C/C++标签中,Debug info 选 Program Database。 3、在Link 标签中,Category选 Debug,选中Debug info 复选框和Microsoft format。 进行了上述设置后,我们就可以像在调试版本中那样设置断点进行测试了,由于代码优化,有些变量观察不到,行的运行顺序可能也会不同。 有一点需要注意:ASSERT宏在 Release 版本中不起作用,在 Release 版本中应该使用 VERIFY 来代替 ASSERT 进行调试。如果发行版本运行有问题,可以先禁止所有代码优化再进行调试。方法二、在需要加断点的地方添加如下汇编语句: __asm int 3 不过调试的时候无法显示C程序,只有asm代码。 此处 int 3 是专门用来设置断点的,是 CPU 定义的,Windows 和 DOS 下的大多数调试器都采用这种方法。本贴不断更新中,希望大家跟贴贡献出更好的调试方法。以便受益者少走弯路......
本文转自:http://www.cppblog.com/mzty/archive/2006/11/19/15439.html
posted @
2013-11-28 10:20 王海光 阅读(636) |
评论 (0) |
编辑 收藏
FTP Wanderer - FTP Client using WININET
![Sample Image](http://www.codeproject.com/KB/IP/ftpwanderer/ftpwanderer.jpg)
Description
This article presents a fully functional implementation of a FTP client. FTP Wanderer is a multithreaded FTP client with the look-and-feel of Windows Explorer, and makes moving files between the Internet and your computer as simple as local file manipulation. This application uses WININET API's to handle the FTP protocol, and while it's not the most elegant way to connect to a FTP server it does the job pretty well.
Features list
- All file transfer requests are handled in the background, so you don't have to wait while files copy to continue browsing the current server. You can even connect to a different server, while file transfers on another server are still in progress.
- The Transfer Manager shows all file transfers currently in progress plus the ones that are in the queue. You can configure how many transfers are processed simultaneously, so you don't run out of resources.
- FTP Wanderer uses the Windows 9x/NT/XP style list boxes for displaying the contents of remote servers. With all familiar system icons and context menus.
- You can specify the number of times it should try to connect, and the time delay between connection attempts.
- Full Drag and Drop support. Simply drag one or more files or even complete folders onto FTP Wanderer's file view area to transfer them to the location of the server that is currently connected.
- Easily configure FTP site settings, like username, password, port, initial remote folder, number of retries and default download folder.
Here's a brief description of some of the important classes of the application:
CMainFrame
CMainFrame contains most of the code for managing the FTP connection. It takes care of connecting to the server; initiate up/downloads and handling other FTP related actions.
CFTPListView
This is just a CListView
derived class that takes care of displaying the files and enabling you to sort them. When the application is busy downloading or sorting a directory listing it can display an animation, just like explorer does.
CConnectDlg
This class (= Site Manager) enables the user to manage FTP sites. You can add/change and delete sites and configure properties such as hostname, port, username and password. The settings of each site are save to disk using serializing.
CTransferManagerDlg
CTransferManagerDlg
(= Transfer manager) takes care of queuing the file transfers. It shows all file transfers currently in progress plus the ones that are in the queue. You can configure how many transfers are processed simultaneously, so you don't run out of resources. Each up/download in handled in a separate thread by initiating aCDownloadThread
or CUploadThread
thread.
CDownloadThread
Each download is handled in the background using a separate thread. CDownloadThread
creates a new connection with the FTP server and displays a progress dialog that tells you the status of the file transfer. When the download is completed (or aborted) it notifies the transfer manager so the UI can be updated.
CUploadThread
Each upload is handled in the background using a separate thread. CUploadThread
creates a new connection with the FTP server and displays a progress dialog that tells you the status of the file transfer. When the upload is completed (or aborted) it notifies the transfer manager so the UI can be updated.
To do list:
While this application has most of the features you might expect in a FTP client, there are a few things left to do:
- Drag and drop support out of the file view area to Explorer. I think this one is not that easy, because this would probably 'block' explorer until the file transfer is completed (any suggestions?).
- Progress indication in the transfer manager. This is not really that hard to do, so this will probably be in the next release.
- Better asynchrony handling of getting directory listing. The UI now does not respond when downloading a huge directory listing.
Contacting the Author
Please send any comments or bug reports to me via
email. For any updates to this article, check my site
here.
Revision history
- 31st July 2002 - Initial revision.
License
posted @
2013-11-19 08:54 王海光 阅读(686) |
评论 (0) |
编辑 收藏
在Window平台上开发任何稍微底层一点的东西,基本上都是Hook满天飞, 普通应用程序如此,安全软件更是如此, 这里简单记录一些常用的Hook技术。
基本上做Windows开发都知道这个API, 它给我们提供了一个拦截系统事件和消息的机会, 并且它可以将我们的DLL注入到其他进程。
但是随着64位时代的到来和Vista之后的UAC机制开启,这个API很多时候不能正常工作了:
首先,32位DLL没法直接注入到64位的应用程序里面, 因为他们的地址空间完全不一样的。当然尽管没法直接注入,但是在权限范围内,系统会尽量以消息的方式让你能收到64位程序的消息事件。
其次,UAC打开的情况下低权限程序没法Hook高权限程序, 实际上低权限程序以高权限程序窗口为Owner创建窗口也会失败, 低权限程序在高权限程序窗口上模拟鼠标键盘也会失败。
有人说我们可以关闭UAC, Win7下你确实可以,但是Win8下微软已经不支持真正关闭UAC, 从这里我们也可以看到微软技术过渡的方式, 中间会提供一个选项来让你慢慢适应,最后再把这个选项关掉, UAC和Aero模式都是如此。
那么我们如何解决这些问题?
对于64位问题 , 解决方法是提供2个DLL,分别可以Hook32和64位程序。
对于权限问题, 解决方法是提升权限, 通过注册系统服务, 由服务程序创建我们的工作进程。这里为什么要创建一个其他进程而不直接在服务进程里干活? 因为Vista后我们有了Session隔离机制,服务程序运行在Session 0,我们的其他程序运行在Session 1, Session 2等, 如果我们直接在服务程序里干活,我们就只能在Session 0里工作。通过创建进程,我们可以在
DuplicateTokenEx后将Token的SessionID设置成目标Session,并且在
CreateProcessAsUser时指定目标WinStation和Desktop, 这样我们就既获得了System权限,并且也可以和当前桌面进程交互了。
很多人可能都不知道这个API, 但是这个API其实挺重要的, 看名字就知道它是Hook事件(Event)的, 具体哪些事件可以看
这里.
为什么说这个API重要, 因为这个API大部分时候没有SetWindowsHookEx的权限问题, 也就是说这个API可以让你Hook到高权限程序的事件, 它同时支持进程内(WINEVENT_INCONTEXT)和进程外(WINEVENT_OUTOFCONTEXT)2种Hook方式, 你可以以进程外的方式Hook到64位程序的事件。
为什么这个API没有权限问题, 因为它是给Accessibility用的, 也就是它是给自动测试和残障工具用的, 所以它要保证有效。
我曾经看到这样一个程序,当任何程序(无论权限高低)有窗口拖动(拖标题栏改变位置或是拖边框改变大小), 程序都能捕获到, 当时很好奇它是怎么做到的?
Spy了下窗口消息, 知道有这样2个消息:WM_ENTERSIZEMOVE和WM_EXITSIZEMOVE表示进入和退出这个事件, 但是那也只能获得自己的消息,其他程序的消息它是如何捕获到的?当时怀疑用的是Hook, 却发现没有DLL注入。查遍了Windows API 也没有发现有API可以查询一个窗口是否在这个拖动状态。最后发现用的是SetWinEventHook的EVENT_SYSTEM_MOVESIZESTART和EVENT_SYSTEM_MOVESIZEEND。
API Hook
常见的API Hook包括2种, 一种是基于PE文件的导入表(IAT), 还有一种是修改前5个字节直接JMP的inline Hook.
对于基于IAT的方式, 原理是PE文件里有个导入表, 代表该模块调用了哪些外部API,模块被加载到内存后, PE加载器会修改该表,地址改成外部API重定位后的真实地址, 我们只要直接把里面的地址改成我们新函数的地址, 就可以完成对相应API的Hook。《Windows核心编程》里第22章有个封装挺好的CAPIHook类,我们可以直接拿来用。
对于基于Jmp方式的inline hook, 原理是修改目标函数的前5个字节, 直接Jmp到我们的新函数。虽然原理挺简单, 但是因为用到了平台相关的汇编代码, 一般人很难写稳定。真正在项目中用还是要求稳定, 所以我们一般用微软封装好的Detours, 对于Detours的原理,这里有篇不错的文章 微软研究院Detour开发包之API拦截技术。
比较一下2种方式:
IAT的方式比较安全简单, 但是只适用于Hook导入函数方式的API。
Inline Hook相对来说复杂点, 但是它能Hook到任何函数(API和内部函数),但是它要求目标函数大于5字节, 同时把握好修改时机或是Freeze其他线程, 因为多线程中改写可能会引起冲突。
还有一种是Hook被调用模块的导出表(EAT), 但是感觉一般用得用的不多。原理是调用模块里IAT中的函数地址是通过被调用模块的EAT获取的, 所以你只要修改了被调用模块的EAT中的函数地址,对方的调用就自然被你Hook了。但是这里有个时机问题, 就是你替换EAT表要足够早,要在IAT使用它之前才行。但是感觉这个行为是由PE加载器决定的, 一般人很难干涉, 不知道大家有什么好方法?
我们一般可以将IAT Hook和EAT Hook结合起来使用, 先枚举所有模块Hook IAT,这样当前已有模块的API都被你Hook了,然后再Hook EAT, 这样后续的模块也被你Hook了(因为要通过EAT获取函数地址)。
如果你只用Hook IAT而不Hook EAT, 当有新模块加载时,你要Hook它的IAT, 所以你就要Hook LoadLibrary(Ex)和GetProcAddress来拦截新模块的加载。所以理论上感觉 Hook EAT不是很有必要, 因为单用Hook IAT已经可以解决我们所有的问题了, HOOK IAT还有一种优势是我们可以过滤某个模块不Hook,而一旦hook EAT, 它就会影响我们所有调用该函数的模块。
COM Hook
Window上因为有很多开发包是以COM方式提供的(比如DirectX), 所以我们就有了拦截COM调用的COM Hook。
因为COM里面很关键的是它的接口是C++里虚表的形式提供的, 所以COM的Hook很多是时候其实就是虚表(vtable)的Hook。
对于COMHook,考虑下面2种case:
一种是我们Hook程序先运行,然后启动某个游戏程序(DirectX 9), 我们想Hook游戏的绘画内容。
这种方式下, 我们可以先Hook API Direct3DCreate9, 然后我们继承于IDirect3D9, 自己实现一个COM对象返回回去, 这样我们就可以拦截到所有对该对象的操作,为所欲为了, 当然我们自己现实的COM对象内部会调用真正的Direct3DCreate9,封装真正的IDirect3D9。
当然有时我们可能不用替代整个COM组件,我们只需要修改其中一个或几个COM函数, 这种情况下我们可以创建真正的IDirect3D9对象后直接修改它的虚表, 把其中某些函数改成我们自己的函数地址就可以了。
还有一种case是游戏程序已经在运行了, 然后才启动我们的Hook进程, 我们怎么样才能Hook到里面的内容?
这种情况下我们首先要对程序内存有比较详细的认识, 才能思考创建出来的D3D对象的虚表位置, 从而进行Hook, 关于程序内存布局,可见我这篇 理解程序内存。
理论上说COM对象如果是以C++接口的方式实现, 虚表会位于PE文件的只读数据节(.rdata), 并且所有该类型的对象都共享该虚表, 所以我们只要创建一个该类型对象,我们就可以获得其他人创建的该类型对象的虚表位置,我们就可以改写该虚表实现Hook(实际操作时需要通过VirtualProtect修改页面的只读属性才能写入)。
但是实际上COM的虚表只是一块内存, 它并不一定是以C++实现, 所以它可以存在于任何内存的任何地方。另外对象的虚表也不一定是所有同类型的对象共享同一虚表, 我们完全可以每个对象都有自己的一份虚表。比如我发现IDirect3D9是大家共享同一虚表的(存在D3D9.dll), 但是IDirect3DDevice9就是每个对象都有自己的虚表了(存在于堆heap)。所以如果你要Hook IDirect3DDevice9接口,通过修改虚表实际上没法实现。
但是尽管有时每个对象的虚表不一样,同类型对象虚表里的函数地址却都是一样的, 所以这种情况下我们可以通过inline Hook直接修改函数代码。当然有些情况下如果是静态链接库,即使函数代码也是每个模块都有自己的一份, 这种情况下就只能反汇编获取虚表和函数的地址了。
posted @
2013-11-08 08:15 王海光 阅读(659) |
评论 (0) |
编辑 收藏
谈到"C#代码反编译",大家可能都会想到 Reflector代码反编译工具,对其应该也不会太陌生;做C#开发,它算得上是一个不可或缺的实用工具。通过它我们可以很方便的查看一个程序集的源代码(这是其最基本的使用,也是大家常用的),还可以方便破解软件...,而我这篇文章要跟大家分享的是:用Reflector将C#的开发的软件[项目](此文中所说的是winForm项目,对于.net项目可以借鉴或参考)的项目代码还原——反编译得到可运行项目源码。 我所做的反编译,并不是'破解',其使用的前提或情况是:公司原来请人开发的'配餐软件(幼儿园版)',现有客户咨询想买,并且软件中存在一些问题需改,——不是本人开发的,该软件也无源码,想修改只能想办法得到软件的源码。具体的'破解'方法记录在下,方便自己以后查看!(ps: '破解'的方法,自己之前就摸索过。但困于反编译之后的error比较多,没有耐心,有的问题感觉比较离奇,试了几次不行就放弃了 ——这就间接的说明:真正的黑客,应该都是比较有耐心的,呵呵..., 但做软件开发,又何尝不需要这样?!)
a.使用到的工具:Reflector,具体的说是:Reflector插件File disassembler(具体是什么和how to use,直接上网查)。
b. 打开Reflector,选择要'破解'的软件(.exe)主程序,再选择并点击 如图:
, 在右侧出现的 程序集(输出)类型选择界面 选择类型'windows Appliction' (因为是winForm应用程序,如果是选择的其它 如'class ...'类库,则需要在之后的步骤中,改变项目属性中的 输出类型,不建议这样操作),如图:![](/images/cppblog_com/wanghaiguang/fanbianyi2.gif)
点击 生成 即可 得到此主程序集的源码,其它的相关程序集再如此操作即可! ——不要以为大功告成,这只是第一步,麻烦的在后面!
c.将生成的源码 在Vs中打开(项目), 先试着运行下,(一般)会报错,排除'xxx程序集不存在'这类的错误,我所遇到的如下:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[CompilerGenerated]
internalclass<PrivateImplementationDetails>{1FF4F699-35E0-4117-BDBC-9E44A1B0F9F5}
{
internalstatic Dictionary<string, int> $$method0x600012e-1;
internalstatic Dictionary<string, int> $$method0x6000137-1;
internalstatic Dictionary<string, int> $$method0x6000137-2;
internalstatic Dictionary<string, int> $$method0x600014a-1;
internalstatic Dictionary<string, int> $$method0x6000169-1;
internalstatic Dictionary<string, int> $$method0x60001b6-1;
internalstatic __StaticArrayInitTypeSize=20 $$method0x6000213-1; // data size: 20 bytes
internalstatic __StaticArrayInitTypeSize=20 $$method0x6000213-2; // data size: 20 bytes
internalstatic __StaticArrayInitTypeSize=20 $$method0x6000213-3; // data size: 20 bytes
internalstatic __StaticArrayInitTypeSize=20 $$method0x6000213-4; // data size: 20 bytes
[StructLayout(LayoutKind.Explicit, Size=20, Pack=1)]
privatestruct __StaticArrayInitTypeSize=20
{
}
}
源码文件中会出现一个 _PrivateImplementationDetails_{1FF4F699-35E0-4117-BDBC-9E44A1B0F9F5}.cs 文件名很长 内容如上的 乱码类,对此解决办法时:注释此类 或直接删除。【程序相关的文件,如图片、数据库等要记得放到项目关联位置,一般在bin/debug/文件夹下】
d. 添加相关程序集的引用,设置启动对象。到这儿,差不多程序就可以 跑起来了,但是还没有完 ——因为 反编译后的代码,文件夹的位置和界面与资源引用之间的关联,基本上都乱了.所以现在要解决的关键问题是:恢复文件间的关联和引用(其它的问题,通过调试就差不多可以解决)。如图:
窗体的.cs和.resx(资源)文件不在同一个文件夹中,对应窗体的.resx文件都加上了项目或解决方案名前缀(zhiyiSystem.) ——This is point! 这就是我们要解决问题的关键:恢复窗体的.cs和.resx文件间的关联,操作大致有以下两步:1.将窗体的.cs和.resx文件放在同一个文件中——即同一目录。2.去掉窗体的.resx文件的前缀 ——即修改文件名,如果是一个个文件去修改,窗体比较多的话,是一件非常重复而无聊的事,于是 就上网找 "批量修改文件名"的工具,下载了一两个感觉都不好用,找不到,只能自己搞了,再说这东西简单,说白了就是 遍历文件夹中文件并'重命名'(代码就不贴出了,文章后 附有 自己写的 "批量修改文件名"工具)。
到此,程序就可以真正跑起来了。别看我写出来,似乎'破解'就是一会儿的事,但我做的时候,却几经折腾 好几次都感觉"算了,又卡住了...",有些或大或小问题,在这里因为时间的原因 及有些步骤一时半会也想不起来了,但主要的方法应该都没有落下,如果有不明白的朋友,可以留言交流,再做解答;也希望在'破解'方法有经验的,能多提些意见,分享下你的‘破解’经验!
后附:
批量修改文件名工具.rar
本文转自:http://www.cnblogs.com/know/archive/2011/03/15/1985026.html
相关连接:http://www.cnblogs.com/verygis/archive/2008/12/02/1346072.html
posted @
2013-10-23 10:23 王海光 阅读(854) |
评论 (0) |
编辑 收藏
看了《深入理解linux内核》的中断与异常,简单总结了下,如果有错误,望指正!
一 什么是中断和异常
异常又叫同步中断,是当指令执行时由cpu控制单元产生的,之所以称之为异常,是因为只有在一条指令结束之后才发出中断(程序执行异常或者系统调用)。
中断又叫异步中断,是由其他硬件设备依照cpu时钟信号随机产生的。
二 高级可编程中断控制器
![](http://images.cnitblog.com/blog/407509/201310/21152018-d5ba3d615eee4296be910c7968265727.jpg)
APIC
每个CPU都有一个本地的APIC,通过IIC bus链接到一个I/O APIC,这个I/O APIC负责处理外部IRQS,分发IRQS给本地APIC。
三 中断与异常处理程序嵌套执行
中断处理程序允许被另一个中断处理程序”中断“,从而引起内核控制路径嵌套执行。但是中断处理程序是不允许发生阻塞,即任务切换的。
中断可以抢占异常处理程序,但异常处理程序不会抢占中断。因为中断处理程序必定处于内核态,如果发生异常,那只能是BUG了,也就是说内核控制路径中异常处理程序不会超过一个。
四 Linux中断描述符
Intel把中断描述符分三类:任务门、中断门、陷阱门,而Linux则分成五类:
- 中断门:Intel的中断门,DPL = 0,描述中断处理程序,通过set_intr_gate宏设置
- 系统门:Intel的陷阱门,DPL = 3,用于系统调用,通过set_system_gate宏设置
- 系统中断门:Intel的中断门,DPL = 3,用于向量3的异常处理,通过set_system_intr_gate宏设置
- 陷阱门:Intel陷阱门,DPL = 0,大部分的异常处理,通过set_trap_gate宏设置
- 任务门:Intel任务门,DPL = 0,对”Double fault“异常处理,通过set_task_gate宏设置
五 异常处理
当cpu产生异常时,会自动根据产生的异常编号在IDT中找对应的异常处理程序,异常处理程序保存大多数寄存器的值,调用异常处理的高级C函数处理该异常,然后通过调用ret_from_exception从异常处理程序退出。
六 中断处理
I/O中断处理程序执行的四个基本过程:
- 在内核态堆栈中保存IRQ的值和寄存器的内容
- 给正在为IRQ线服务的PIC发送一个应答,这将允许该PIC进一步发中断
- 执行共享该IRQ的所有设备的中断服务例程(ISR)
七 IRQ数据结构
![](http://images.cnitblog.com/blog/407509/201310/21153644-8982effaf1e942c492e101f1fb0ff1ff.jpg)
IRQ数据结构
hw_irq_controller是对PIC进程控制的一些函数,包括应答PIC什么的。action指向的是一个irqaction链,每个irqaction描述一个设备的服务例程。irq_desc_t中的state字段保证了同一时刻只有一个设备会拥有该IRQ,正在处理该IRQ的CPU会禁用这条IRQ(本地),其它cpu还是可以接受该IRQ的请求,不过由于此时state的状态为IRQ_INPROGRESS,所以新的IRQ请求会在其它的CPU上应答,但不会处理,也就是该新的IRQ处理会被延迟到处理同一个IRQ的前面一个CPU上执行。能这样做是因为IRQ的数据结构是所有CPU所共享的。
八 多种类型的内核栈
如果编译内核设置内核栈为8k,那么进程的内核栈被用于所有类型的内核控制路径。如果内核栈为4k,则内核使用3种类型的内核栈:异常栈,用于处理异常,每个进程一个;硬中断栈,用于处理中断,每个cpu一个;软中断栈,用于出来延迟函数,每个cpu一个。
九 软中断
1 为什么要引进软中断机制,用前面的中断机制不就可以了吗(老版本的linux就没有软中断机制)?
从前面的中断处理中可以看出,一个中断处理程序的几个中断服务例程(每个设备一个)是串行执行的,如果某个处理例程执行的时间比较长,而后面的例程又很紧急,那么会导致这个紧急的例程汇编延迟比较久的时间。所以如果能把一些服务例程中不是很紧急但又花费比较长的操作延迟到执行完该IRQ上所有中断服务例程之后执行,那么一些紧急的,花费时间短(一般紧急的操作所需的时间都是比较短的)的例程就可以得到快速的响应。还有就是对于某个设备的中断服务例程,如果它的服务例程服务时间过长,cpu在执行该服务例程时是会中断本地cpu对该设备的中断或者整个本地中断,这样会导致很多中断会得不到快速的响应。
2 软中断使用的关键数据结构
softirq_vec数组,每个数组元素类型为softirq_action,该数组总共有32个元素,目前只用了前面六个。softirq_action数据结构包含两个字段:指向软中断处理函数的action指针和指向软中断函数需要的通用数据结构的data指针,这是cpu共享的。
每个进程描述的thread_info字段中的preempt_count字段,该字段被编码来表示三个不同的计数器和一个标志。0-7位表示是否允许抢占内核,8-15表示是否正在处理软中断,16-27表示硬件中断控制路径嵌套数,28为是PREEMPT_ACTIVE标志。每个进程有一个。
另一个是每个cpu都有的32位掩码,存放在irq_cpustat_t数据结构中的__softirq_pending字段,32位,每一位表示softirq_vec数组中的对应的软中断函数是否已激活。irq_cpustat_t存放在irq_stat数组中,每个cpu对于数组中的一个irq_cpustat_t。每个cpu一个。
3 软中断可延迟函数的四个操作
- 初始化,定义一个新的可延迟函数,并加入到softirq_vec数组中,所有cpu共享该softirq_vec。
- 激活,标记一个可延迟函数为”挂起“,通过前面描述的__softirq_pending字段。
- 屏蔽,有选择地屏蔽一个可延迟函数,即使它被激活。它是通过前面的preempt_count字段或者关闭本地中断(延迟函数一般是通过中断处理程序激活的,如果没有中断处理程序执行,自然也就不会有延迟函数的激活)来实现的。
- 执行,执行一个挂起的可延迟函数和同类型的其它挂起的可延迟函数。通过do_softirq实现。
激活和执行可延迟函数必须要在同一个cpu上,从前面激活和执行可以看出这一点。因为__softirq_pending是每个cpu一个,所有在特定cpu激活的延迟函数,只有在该cpu上的__softirq_pending标记激活,而其它cpu是不知道该函数被激活的,也就不会去执行该函数了。
4 linux现有的六种软中断
处理高级优先级的tasklet软中断HI_SOFTIRQ,在softirq_vec数组的下标为0;和时钟中断关联的tasklet软中断TIMER_SOFTIRQ,在softirq_vec数组的下标为1;把数据包传送到网卡软中断NET_TX_SOFTIRQ,在softirq_vec数组的下标为2;从网卡接收数据包的软中断NET_RX_SOFTIRQ,在softirq_vec数组的下标为3;SCSI命令的后天中断处理的软中断SCSI_SOFTIRQ,在softirq_vec数组的下标为4;处理常规tasklet软中断TASKLET_SOFTIRQ,在softirq_vec数组的下标为5;它们的优先级是从高到低的。
5 检查是否有软中断挂起(也叫激活)的时机
- 内核调用local_bh_enable激活本地软中断
- smp_apic_timer_interrupt处理完本地定时中断
- cpu处理CALL_FUNCTION_VECTOR中断处理
- 当一个特殊的ksoftirq/n内核线程被唤醒。
ksoftirq/n内核线程是专门用来处理激活的软中断的,每个cpu有一个。
十 tasklet
tasklet是在软中断的基础上实现的。正如前面说的linux现有的六种软中断,其中HI_SOFTIRQ和TASKLET_SOFTIRQ软中断就是tasklet。
对于每种tasklet(HI_SOFTIRQ和TASKLET_SOFTIRQ),每个cpu都有一个tasklet_head类型来描述这种tasklet。tasklet_head类型指向了一个由tasklet类型组成的链表,而每个tasklet类型描述了该tasklet要执行的函数和函数需要的数据以及tasklet的状态(状态表示同类型的tasklet是否在运行等)。
当do_softirq处理软中断时,如果相应的HI_SOFTIRQ和TASKLET_SOFTIRQ软中断被激活,就会调用对应的软中断函数tasklet_hi_action和tasklet_action。而这两个函数处理的数据正是HI_SOFTIRQ和TASKLET_SOFTIRQ类型tasklet所对应的tasklet_head类型指向的tasklett链表。
为了能够写自己的tasklet函数并加入到对应tasklet类型的tasklet_head指向的链表中,需要调用tasklet_init来初始化新的tasklet和调用tasklet_schedule或tasklet_hi_schedule来把我们自定义的tasklet加入到对应的tasklet_head指向的链表中并激活该tasklet类型所对应的软中断。
所以说tasklet是在软中断的基础上实现的,但不同的是软中断时静态分配的(linux分配了6个软中断),而tasklet是可以动态加入和删除tasklet的。
十一 工作队列
每个工作队列,在每个cpu上都有一个cpu_workqueue_struct结构来描述,即对于同一个工作队列,每个cpu都有该队列的一个拷贝。cpu_workqueue_struct中的worklist成员指向了由work_struct结构组成的链表,这个链表描述的是该工作队列被挂起的函数,等待工作者线程去执行。
通过调用create_workqueue创建工作队列。如果是smp,则每个cpu都会创建一个工作队列和为该工作队列工作的工作者线程。该工作者线程一直阻塞,直到有函数被加入到工作队列中去,工作者线程执行了函数之后就把函数从工作队列中删除。
可以通过queue_work函数把一个函数加入到工作队列中去。
由于仅仅为了一个函数就创建一个工作队列开销太大,所以内核预定义了一个叫events的工作队列,为该队列工作的工作者线程叫events/n,每个cpu一个。还有就是专门为块设备使用的kblocked工作队列。
十二 软中断与工作队列的区别
从上面可以看出,软中断和工作队列十分的相似,都是对服务例程中某些耗时的操延后处理。主要区别在于,软中断的可延迟函数是在中断上下文中执行的,而工作者队列的函数则是在进程上下文中执行的,也就是说软中断的延迟函数执行期间不允许内核被抢占,而工作队列则是可以的。
本文转自:http://www.cnblogs.com/chengxuyuancc/p/3380922.html
posted @
2013-10-22 08:32 王海光 阅读(556) |
评论 (0) |
编辑 收藏
示例代码:
long begin,end;
double time;
begin = clock();
{
//查看代码
}
end = clock();
time = (double)(end - begin) / CLOCKS_PER_SEC;
Logger->Info( "%f seconds\n", time );
其他参考:
#include<iostream>
include<windows.h>
using namespace std;
int main()
{
DWORD start_time=GetTickCount();
{
//此处为被测试代码
}
DWORD end_time=GetTickCount();
cout<<"The run time is:"<<(end_time-start_time)<<"ms!"<<endl;//输出运行时间
return 0;
}
#include<iostream>
#include<time.h>
using namespace std;
int main()
{
clock_t start_time=clock();
{
//被测试代码
}
clock_t end_time=clock();
cout<< "Running time is: "<<static_cast<double>(end_time- start_time)/CLOCKS_PER_SEC*1000<<"ms"<<endl;//输出运行时间
return 0;
}
posted @
2013-10-22 08:20 王海光 阅读(812) |
评论 (0) |
编辑 收藏
STL中的容器按存储方式分为两类:一类是按以数组形式存储的容器(如:vector,deque);另一类是以不连续的节点形式存储的容器(如:list,map,set)。 在使用erase方法删除元素时,迭代器有时候会失效。在Effective STL,条款9,找到了erase容器中元素的原则。
1. 对于关联容器(如map, set, multimap,multiset),删除当前的iterator,仅仅会使当前的iterator失效,只要在erase时,递增当前iterator即可。这是因为map之类的容器,使用了红黑树来实现,插入、删除一个结点不会对其他结点造成影响。
错误的使用方法:
std::map<string, string> mapTest;
std::map<string, string>::iterator iter;
for ( iter = mapTest.begin();iter != mapTest.end(); iter ++ )
{
if ( iter->second == "test" )
{
mapTest.erase( iter );
}
}
正确的使用方法1:
std::map<string, string> mapTest;
std::map<string, string>::iterator iter;
for ( iter = mapTest.begin();iter != mapTest.end();)
{
if ( iter->second == "test" )
{
mapTest.erase( iter++ );
}
else
{
iter++; // Use Pre Increment for efficiency.
}
}
因为iter传给erase方法的是一个副本,iter++会指向下一个元素。 正确的使用方法2:
std::map<string, string> mapTest;
std::map<string, string>::iterator iter;
for ( iter = mapTest.begin();iter != mapTest.end();)
{
if ( iter->second == "test" )
{
iter = mapTest.erase( iter );
}
else
{
++iter; // Use Pre Increment for efficiency.
}
}
2. 对于序列式容器(如vector,deque),删除当前的iterator会使后面所有元素的iterator都失效。这是因为vetor,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置。还好erase方法可以返回下一个有效的iterator。 3. 对于list来说,它使用了不连续分配的内存,并且它的erase方法也会返回下一个有效的iterator,因此上面两种正确的方法都可以使用。 其他链接:
http://www.cppblog.com/Herbert/archive/2009/01/08/70479.html
其他链接:
http://blog.csdn.net/kay226/article/details/6126515
其他链接:
http://www.cppblog.com/humanchao/archive/2013/04/22/199630.html
posted @
2013-10-14 18:22 王海光 阅读(977) |
评论 (0) |
编辑 收藏
在MFC开发过程中使用ComboBox,有时会根据需求要求只能输入数字,并且要求数字在一定的范围内,例如一个用于选择小时的ComboBox,时间范围为00到23,那么该如何实现?
代码如下:
在Dialog.h中:
……
CComboBox m_cbHour;// 声明CComboBox类型作为CDialog类的成员变量
……
afx_msg void OnCbnEditchangeCbHour();
afx_msg void OnCbnEditupdateCbHour();
在Dialog.cpp中:
……
DDX_Control(pDX, IDC_CB_HOUR, m_cbHour);// 数据交换
……
ON_CBN_EDITCHANGE(IDC_CB_HOUR, &CDialog::OnCbnEditchangeCbHour)//添加消息映射,用于CComboBox内容改变中
ON_CBN_EDITUPDATE(IDC_CB_HOUR, &CCDialog::OnCbnEditupdateCbHour)//添加消息映射,用于CComboBox内容更新
……
下面来具体实现函数:
循环遍历CComboBox中已经输入的字符如果发现含有非数字则,置为“00”,使用循环的目的是为了防止用户跳着输(大概只有测试人员才会跳着输)。
将for循环改为for (int i = 0; i < (strTmp.GetLength()); i++)遇到非数字字符就设置为00;
当CComboBox更新数据时判断数字范围,如果超过边界,则设置值为最近边界值,至此一个只接受可控范围数字的CComboBox就完成了,但是在WinCE开发中却有一个问题,就是所有的汉字都输不进去,但是“头”这个汉字却能输入,不知道这是不是一个BUG,如果在代码中对“头”做特别判断,那这段程序也太奇怪了!
本文转自:http://blog.csdn.net/wyunteng/article/details/6370882
posted @
2013-09-25 17:39 王海光 阅读(3362) |
评论 (1) |
编辑 收藏
在用SetWindowText设置控件内容时,有时候会出字体重叠问题,调用次数越多,重叠越严重。以下为解决方法。
方法一:RedrawWindow()1 GetDlgItem(IDC_STATIC)->SetWindowText("your string");
2 GetDlgItem(IDC_STATIC)->GetParent()->RedrawWindow();
缺点:窗口刷新太频繁,一闪一闪,效果不太好。
方法二:局部刷新1 void YourDlg::RefreshControl(UINT uCtlID)
2 {
3 CRect rc;
4 GetDlgItem(uCtlID)->GetWindowRect(&rc);
5 ScreenToClient(&rc);
6 InvalidateRect(rc);
7 }
方法三:隐藏和显示1 GetDlgItem(IDC_STATIC)->ShowWindow(SW_HIDE);
2 GetDlgItem(IDC_STATIC)->ShowWindow(SW_SHOW);
posted @
2013-09-24 10:31 王海光 阅读(3577) |
评论 (0) |
编辑 收藏
方法一:
CFile类的成员变量:
m_hFile:表示一个打开文件的操作系统文件句柄。通过对m_hFile 与 CFile::hFileNull的比较来判断该文件是否已经打开。
示例代码:
1 CString strFilename = _T("D:\\大学语文.docx");
2 CFile file;
3 file.Open(strFilename,CFile::modeReadWrite);//
4
6 if (file.m_hFile != CFile::hFileNull)
7 {
8 file.Close();
9 }
10 else
11 {
12 printf("File Already Close \n");
13 }
方法二:
利用file.GetFileName().IsEmpty()来判断
示例代码:
1 CString strFilename = _T("D:\\大学语文.docx");
2 CFile file;
3 file.Open(strFilename,CFile::modeReadWrite);//
4
5 if (!file.GetFileName().IsEmpty())
6 {
7 file.Close();
8 }
9 else
10 {
11 printf("File Already Close \n");
12 }
方法三:
通过设置成员变量来记录文件是否被打开。如BOOL bIsFileOpen;默认是FALSE,
打开成功,把它置为TRUE;否则置为FALSE;
然后在程序里面判断就可以了。关闭后置bIsFileOpen为FALSE,
posted @
2013-09-23 16:46 王海光 阅读(9942) |
评论 (0) |
编辑 收藏