Chinaunix首页 | 论坛 | 博客
  • 博客访问: 370714
  • 博文数量: 71
  • 博客积分: 4691
  • 博客等级: 上校
  • 技术积分: 935
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-14 15:14
个人简介

who am i ... i'm back.

文章分类

全部博文(71)

文章存档

2014年(4)

2011年(1)

2010年(22)

2009年(17)

2008年(27)

我的朋友

分类: WINDOWS

2010-07-16 15:37:05

1.Windows的内存结构
Windows系统中的每个进程都被赋予它自己的虚拟地址空间。对于32位进程来说,这个地址空间是4GB,因为32位指针可以拥有从 0x00000000至0xFFFFFFFF之间的任何一个值。对于64位进程来说,则这个空间是16EB。由于每个进程可以接收它自己的私有的地址空 间,因此当进程中的一个线程正在运行时,该线程也只能访问只属于它的进程的内存。属于所有其他进程的内存则隐藏着,并且不能被访问。

每 个进程的虚拟地址空间都要划分成各个分区,地址空间的分区时根据操作系统的基本实现来进行的,不同的windows内核,其分区也略有不同。下面以32位 windows 2000 (x86和alpha处理器)

另外,如果用户想把用户方式分区扩大到3GB,则在x86的windows 2000 advanced server版本和windows 2000 data center版本中可以加入/3GB开关到BOOT.INI文件中。使用/3GB开关后,将减少系统能够创建的线程、堆栈和其他资源的数量。此外,系统最 多使用16GB的RAM,而通常情况下可以使用64GB的RAM。因为内核方式中没有足够的虚拟空间来管理更多的RAM。

2.地址空 间中的区域
当进程被创建并被赋予它的地址空间时,该可用空间的主体是空闲的,未分配的。若要使用该地址空间的各个部分,必须要调用virtualAlloc 函数来分配它里边的各个区域。对每一个地址空间的区域进行分配的操作称为保留(reserve)

当你保留地址空间的一个区域时,该区域必须是系统的页面大小的倍数,而且分配边界必须从一个分配粒度开始。例如x86的页面大小为4KB,分配粒度为 64KB。

若要使用已保留的地址空间区域,则必须分配物理存储器,然后将该物理存储器映射到已保留的地址空间区域。这个过程叫提交物理存储器。也调用 VirtualAlloc函数,但和前面保留的输入参数有所区别,可以自己查此函数。当然用户也可以在保留地址空间的同时提交物理存储器。

在较老的操作系统中,物理存储器被视为计算机所有的RAM的容量。如果计算机有16M的RAM,则加载和运行的应用程序最多可以使用16M的RAM。今天 的操作系统则使得磁盘空间看上去像内存一样。磁盘上的文件通常称为页文件,它包含了可以供所有进程使用的虚拟内存。(用户可以在“我的电脑->属性 ->高级”里面查看虚拟内存的页文件信息)

这样,当一个应用程序通过调用VirtrualAlloc函数,将物理存储器提交给 地址空间的一个区域时,地址空间实际上是从硬盘上的一个文件中进行分配的。

当用户进程中的一个线程试图访问进程的地址空间中的一个数据块的时候。一般会发生两种情况:

1. 线程试图访问的数据是在RAM中,则cpu只需要将虚拟地址映射到内存的物理地址中,然后执行需要的访问。

2. 数据不在RAM中,而是放在页文件的某个地方。这时候,访问引起页面失效,cpu将通知操作系统,操作系统就从RAM中寻找一个空白页,如果找不到空白 页,则必须释放一个页。如果该页面没有被修改过,则可以直接释放,否则必须先把此页面从RAM拷贝到页面交换文件,然后系统进入该页文件,找出需要访问的 数据,并将数据加载到空闲的内存页面。然后,操作系统更新它的用于指明数据的虚拟内存地址现在已经映射到RAM中的相应的物理存储器地址中的表。

3. Windows的内存管理方法
windows提供了3种方法来进行内存管理:

l 虚拟内存,最适合用来管理大型对象或者结构数组

l 内存映射文件,最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行多个进程之间共享数据。

l 内存堆栈,最适合用来管理大量的小对象。

3.1 虚拟内存

虚拟内存的使用主要有以下几个步骤:

1. 在地址空间保留一个区域,调用函数VirtualAlloc

2. 在保留区域中的提交物理存储器,当保留一个区域后,必须将物理存储器提交给该区域,然后才能访问该区域中包含的内存地址。系统从它的页文件中将已提交的物 理存储器分配给一个区域。仍旧调用函数VirtualAlloc具体参数设置可以见msdn,当然,用户也可以一次性地进行操作保留区域和提交物理存储 器。

3. 回收虚拟内存和释放地址空间区域,调用VirtualFree函数,并且,如果要释放一个区域,必须释放该区域保留地所有地址空间。当然用户也可以只回收 物理存储器而不释放区域,仍旧调用VirtualFree函数,但参数传入不同。

3.2 内存映射文件

与虚拟内存一样,内存映射文件可以用来保留一个地址空间的区域,并将物理存储器提交给该区域。他们之间的区别是,物理存储器来自一个已经位于磁盘上的文 件,而不是系统的页文件。一旦该文件被映射,就可以访问它,就像整个文件被加载到了内存一样。

内存映射文件一般用于3个不同的目的:

1. 系统使用内存映射文件,以便加载和执行.exe和DLL文件。这可以大大节省页文件空间和应用程序启动运行所需的时间

2. 可以使用内存映射文件来访问磁盘上的数据文件。这使你可以不必对文件执行i/o操作,并且可以不必对文件内容进行缓存

3. 可以使用内存映射文件,使同一台计算机上运行的多个进程能够相互之间共享数据。

若要使用内存映射文件,必须执行下列操作步骤:

1) 创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存映射文件的文件 (CreateFile函数)

2) 创建一个文件映射内核对象,告诉系统该文件的大小和你打算如何访问该文件

(CreateFileMapping函数)

3) 让系统将文件映射对象的全部或一部分映射到你的地址空间

(MapViewOfFile函数,要求文件的位移是分配粒度的倍数)

当完成对内存映射文件的使用时,必须执行下面的这些步骤将它清除:

4)告诉系统从你的进程的地址空间中撤销文件映射内核对象的 映象

(UnmapViewOfFile函数)

5)关闭文件映射内核对象

(CloseHandle 函数,第2)步创建的对象)

6)关闭文件内核对象

(CloseHandle函数,第1)步创建的对象)

利 用内存映射文件,还可以实现进程之间的数据共享。数据共享的方法是通过让两个或多个进程映射同一个文件映射对象的视图,这也意味着他们将共享物理 存储器的同一个页面。另外,用户也可以创建由系统的页文件支持的内存映射文件,而不是由专用硬盘文件支持的内存映射文件。这样,就不需要调用 CreateFile函数,只需要给CreateFileMapping的Hfile参数传递INVALID_HANDLE_VALUE,并传递一个以0 结尾的字符串作为pszName参数。别的进程就可以用CreateFileMapping或者OpenFilemapping函数。

3.3 堆栈

堆栈可以用来分配许多较小的数据块,例如对链接表和链接树进行管理等。堆栈的优点是,可以不考虑分配粒度和页面边界之类的问题。堆栈的缺点是,分配和释放 内存块的速度比其他机制要慢,并且无法直接控制物理存储器的提交和回收。

当进程初始化时,系统在进程的地址空间中创建一个堆栈。该堆栈为进程的默认堆栈。按照默认设置,该堆栈的地址空间区域的大小是1MB。系统可以扩大进程的 默认堆栈。由于进程的默认堆栈可以供许多windows函数调用,因此对默认堆栈的访问是按顺序进行的。也就是,系统必须保证在规定的时间内,每次只有一 个线程能分配和释放默认堆栈中的内存块 。当然,用户也可以在进程的地址空间中创建一些辅助堆栈。

堆栈的一些操作函数如下(具体可以查msdn):

1.创建堆栈 HeapCreate

2. 从堆栈中分配内存块 HeapAlloc

3. 改变内存块的大小 HeapReAlloc

4. 释放内存块 HeapFree

5. 撤销堆栈 HeapDestroy










---------------------------------------------------------------------------------------

Winform 小技巧

未添加"(静态)"的成员为实例成员

获取程序可执行文件的路 径:System.Reflection.Assembly.GetEntryAssembly().Location


获取程序 所在文件夹的路径:System.Environment.CurrentDirectory


获取Windows系统特殊文件 夹的路径:System.Environment.GetFolderPath( Environment.SpecialFolder  folder )


获 取鼠标的屏幕坐标:System.Windows.Forms.Control.MousePosition(静 态)


屏幕坐标转换为工作区坐标:System.Windows.Forms.Control.PointToClient(Point p)


获取程序集中的资源:
System.Resources.ResourceManager 类


控 件和对象属性双向绑定:对象实需现接口System.ComponentModel.INotifyPropertyChanged接口


向 活动应用程序发送击键:System.Windows.Forms.SendKeys.Send (string keys) (静态)


窗体显示后改变窗体的位置:
1.修改Form.StartPosition 为System.Windows.Forms.FormStartPosition.Manual
2.修改Form.Location

查 看窗体样式:利用VS自带工具spy++


窗体显示在另一窗体之上:Form1.Owner = Form2。这样Form1就显示在Form2上,并随Form2一起关闭和最小化。


闪烁窗体:
调 用Api"FlashWindow"
DllImport("user32.dll")]  
private static extern bool FlashWindow(IntPtr hwnd, bool bInvert);
public static void FlashWindow(System.Windows.Forms.Form window)
{
   FlashWindow(window.Handle, true);
}


窗体没有焦点也触发 按键事件:
  Form.KeyPreview设置为true,窗体将接受所有控件的按键事件。要仅在窗体级别处理键盘 事件并且不允许控件接收键盘事件,请将窗体的 KeyPress 事件处理程序中的 KeyPressEventArgs.Handled 属性设置为 true。


窗体全屏:
form.WindowState = FormWindowState.Normal;
Rectangle rect = Screen.PrimaryScreen.Bounds;
form.Location = rect.Location;
form.Size =rect.Size;
注意:全屏后如何恢复到之前状态。


在资源管理器中打开文件夹的父文 件夹并使该文件夹处于选中状态:System.Diagnostics.Process.Start("explorer.exe", dirPath);


在资源管理器中打开文件的文件夹并使该文件处于选中状 态:System.Diagnostics.Process.Start("explorer.exe", "/select," + filePath);


关闭窗体但不出发FormClosing事件:调用Form.Dispose()方法关闭窗体


在 主窗体之前显示窗体:
  static   void   Main()    
  {  
    Form  frmMain=new   Form();  
    Form  frmLgn=new   Form();  
    frmLgn.ShowDialog();  
    Application.Run(   frmMain   );   
  }


使控件不显示聚焦框:
如果控件有ShowFocusCues属 性则可以通过覆写该属性实现。
protected override bool ShowFocusCues
{
     get
      {
          // 不显示聚焦框
          return false;
      }
}       


过滤Windows消 息:System.Windows.Forms.IMessageFilter


NotifyIcon不显示:设置 NotifyIcon.Ico属性


PropertyGrid禁止编辑:
在 PropertyGrid的ValueChanged事件中添加如下代码
private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            e.ChangedItem.PropertyDescriptor.SetValue(this.propertyGrid1.SelectedObject, e.OldValue);
}


使窗体不显示标题:
重写 CreateParams属性,WS_CAPTION是一个常量。
private const int WS_CAPTION = 0XC00000;
protected override CreateParams CreateParams
 {
        get
        {
             CreateParams createParams;
             createParams = base.CreateParams;
             createParams.Style ^= WS_CAPTION;

             return createParams;
        }
 }


移 动无标题栏窗体
在窗体的mousemove事件中处理,不能放进mousedown中,否则会无法接受鼠标双击消息
Form_MouseMove()
{
       ReleaseCapture();
       SendMessage(this.FindForm().Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
}
[DllImport("user32.dll")]
private static extern bool ReleaseCapture();
[DllImport("user32.dll")]
private static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_MOVE = 0xF010;
private const int HTCAPTION = 0x0002;


强 制重新绘制窗体及其内容的两种方法:
1.将 Invalidate 方法的重载之一与 Update 方法一起使用。
2 调用 Refresh 方法,此方法强制控件重新绘制其自身及其所有子级。这等效于将 Invalidate 方法设置为 true 并将该方法与 Update 一起使用。
Invalidate 方法控制绘制或重新绘制的内容。Update 方法控制发生绘制或重新绘制的时间。如果将 Invalidate 和 Update 方法一起使用,而不是调用 Refresh,则重新绘制的内容取决于您使用的 Invalidate 的重载。Update 方法仅仅是强制立即绘制控件,而 Invalidate 方法则控制当您调用 Update 方法时所绘制的内容。


自 己绘制树节点:
设置TreeView.DrawNode属性为OwnerDrawText或OwnerDrawAll, 在TreeViewDrawNode。
在TreeView.DrawNode 事件中进行绘制。只有当节点可见时才绘制,否则可能会在TreeView左上角绘制不可见的节点,使显示出现问题。
private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
  if (e.Node.IsVisible)//判断节点是否可见   
  {
    if (e.State == 0)
     {
      //在这里添加未选中节点绘制代码,节点在未选中状态下State为 0,System.Windows.Forms.TreeNodeStates枚举中并不包括0,
    }
    else
     {
      //其他状态的绘制代码
    }
  }
}


在 IE进程中打开网页:
仅对IE6有效
//在框架"kaka"中打开bing,这时会打开IE,并显示网页 。
WebBrowser.Navigate("","kaka");
// 打开百度也指定同一个框架"kaka",就会更改已打开的IE的Uri为百度。
WebBrowser.Navigate("","kaka",);


创 建窗体大小的画布:
重绘窗体边框需要创建和窗体一样大的画布。
以下是重载OnPaintBackground方 法,在绘制背景时绘制边框
protected override void OnPaintBackground(PaintEventArgs e)
{
   IntPtr  windowDC = GetWindowDC(this.Handle);
  try
   {
    using (Graphics graphics = Graphics.FromHdc(windowDC))
     {
     //绘制边框
    }
  }
  finally
  {
     ReleaseDC(this.Handle, windowDC);
  }
}
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_MOVE = 0xF010;
private const int HTCAPTION = 0x0002;

为窗体和控件设置双缓冲:
对 于大多数应用程序,.NET Framework 提供的默认双缓冲将提供最佳效果。默认情况下,标准 Windows 窗体控件是双缓冲的。可以通过两种方法对窗体和所创作的控件启用默认双缓冲。一种方法是将 DoubleBuffered 属性设置为 true,另一种方法是通过调用 SetStyle 方法将 OptimizedDoubleBuffer 标志设置为 true。两种方法都将为窗体或控件启用默认双缓冲并提供无闪烁的图形呈 现。建议仅对已为其编写所有呈现代码的自定义控件调用 SetStyle 方法。


手动呈现双缓冲:
参考以下类型
BufferedGraphics
BufferedGraphicsContext
BufferedGraphicsManager


生成字符串的哈希编 码:System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFiles(string content,string format)


获取配置文件的路 径:AppDomain.CurrentDomain.SetupInformation.ConfigurationFile


posted @ 2009-08-12 09:00 lovegis 阅读(212) | 评论(0) | 编 辑

2009年8月11日 #

屏 蔽窗体关闭按钮

[DllImport("USER32.DLL")]
  private static extern IntPtr GetSystemMenu(IntPtr hWnd, UInt32 bRevert);
  [DllImport("USER32.DLL")]
  private static extern UInt32 RemoveMenu(IntPtr hMenu, UInt32 nPosition, UInt32 wFlags);

  private const UInt32 SC_CLOSE = 0x0000F060;
  private const UInt32 MF_BYCOMMAND = 0x00000000;

  public Form1()
  {   
   InitializeComponent();

   IntPtr hMenu = GetSystemMenu(this.Handle, 0);
   RemoveMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
   }

posted @ 2009-08-11 10:21 lovegis 阅读(98) | 评论(0) | 编 辑

同 步要领

     摘要: 同步要领(转载:) 下面的表格列展了.NET对协调或同步线程动作的可用的工具: 简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程完成 ...  阅读 全文

posted @ 2009-08-11 09:01 lovegis 阅读(109) | 评论(0) | 编 辑

2009年8月10日 #

winsocket.h

     摘要: winsock.h /* WINSOCK.H--definitions to be used with the WINSOCK.DLL * Copyright 1993 - 1998 Microsoft Corp. All rights reserved. * * This header file corresponds to version 1.1 of the Windows Socke...  阅读 全文

posted @ 2009-08-10 08:47 lovegis 阅读(76) | 评论(0) | 编 辑

2009年6月3日 #

异 步SOCKET心得

异步SOCKET心得

 

 

将最近在网络通讯项目中对Socket使用的一些经验总结如下。

 

 

1.         不要在异步完成方法中进行耗时的调用。

 

这里的异步完成方法在.NET FRAMEWORK 2.0对应于异步操作的回调方法,以Socket.BeginSend方法为例如下。

public IAsyncResult BeginSend(

      byte[] buffer,

      int offset,

      int size,

      socketFlags,

      out errorCode,

      AsyncCallback callback,

      Object state

)

参数“callback”是一个委托,代表在发送完毕后,在IOCP线程中将要调用的回调方法。

 

.NET FRAMEWORK 2.0 SP1SP2中添加了一个类SocketAsyncEventArgs,它的事件Completed同样是在IOCP线程中触发的,用于在IOCP线程中处理异步操作的完成。

 

后文所说的异步完成方法就对应callback委托和Completed事件。

 

下面说为什么不能在异步完成方法中进行耗时的操作。

 

IOCP线程开启的个数和CPU核数有关(经过试验), 默认情况下开启的IOCP线程数据和CPU数目一致。即时调用了ThreadPool.SetMaxThreadsThreadPool.SetMaxThreads方法 ,单核CPU上 只默认开启一个IOCP线程,双核则开启两个。当然开启的IOCP线程数也可能超过CPU个数。这发生在已开启的IOCP线程处于阻塞状态(不会分配CPU时 间),只有当所有已开启的IOCP线程处于阻塞状态时才会有开启新的IOCP线程。

 

可以确定在任意时刻可执行(为处于休眠状态)IOCP线程的数目都不会超过CPU核数。所有异步操作完成后都在IOCP队列排队,等待取出后调用异步完成方法。

 

 

2.         异步建立连接。

 

当远程端口未建立监听时,同步建立连接(Connect)将会很耗时、尤其是当远程主机并未开启时。

 

 

3.         同步接受(Accept)连入的套接字。

 

异步接受(AcceptAsyncBeginAccept)连 入的套接字,接受完成后要在IOCP队列排队,前面说过所有的异步操作完成后都要在IOCP队列排队,可能接受完成前面已经有很多需要处理的完成操作, 可能会很耗很多时间。这样就造成了监听队列中套接字不断增加,达到最大值后,便无法连入新的套接字。造成远程主机连接失败。在一个循环中同步接受连入的套 接字,可以尽快的从监听队列中取出连入的套接字,加快对连接的响应。

阅读(1561) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~