`
freesoftman
  • 浏览: 319898 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

VC++中进程与多进程管理地办法

阅读更多

文章来自于:http://hi.baidu.com/ranzoo/blog/item/663f1d3f25c72bed55e7236d.html

摘要: 本文主要介绍了多任务管理中的多进程管理技术,对进程的互斥运行、子进程的创建与结束等作了较详细的阐述。

  关键词: VC++6.0;进程;环境变量;子进程
  进程

  进程是当前操作系统下一个被加载到内存的、正在运行的应用程序的实例。每一个进程都是由内核对象和地址空间所组成的,内核对象可以让系统在其内存放有关进程的统计信息并使系统能够以此来管理进程,而地址空间则包括了所有程序模块的代码和数据以及线程堆栈、堆分配空间等动态分配的空间。进程仅仅是一个存在,是不能独自完成任何操作的,必须拥有至少一个在其环境下运行的线程,并由其负责执行在进程地址空间内的代码。在进程启动的同时即同时启动了一个线程,该线程被称作主线程或是执行线程,由此线程可以继续创建子线程。如果主线程退出,那么进程也就没有存在的可能了,系统将自动撤消该进程并完成对其地址空间的释放。

  加载到进程地址空间的每一个可执行文件或动态链接库文件的映象都会被分配一个与之相关联的全局唯一的实例句柄(Hinstance)。该实例句柄实际是一个记录有进程加载位置的基本内存地址。进程的实例句柄在程序入口函数WinMain()中通过第一个参数HINSTANCE hinstExe传递,其实际值即为进程所使用的基本地址空间的地址。对于VC++链接程序所链接产生的程序,其默认的基本地址空间地址为0x00400000,如没有必要一般不要修改该值。在程序中,可以通过GetModuleHandle()函数得到指定模块所使用的基本地址空间。


  子进程的创建

  进程的创建通过CreateProcess()函数来实现,CreateProcess()通过创建一个新的进程及在其地址空间内运行的主线程来启动并运行一个新的程序。具体的,在执行CreateProcess()函数时,首先由操作系统负责创建一个进程内核对象,初始化计数为1,并立即为新进程创建一块虚拟地址空间。随后将可执行文件或其他任何必要的动态链接库文件的代码和数据装载到该地址空间中。在创建主线程时,也是首先由系统负责创建一个线程内核对象,并初始化为1。最后启动主线程并执行进程的入口函数WinMain(),完成对进程和执行线程的创建。

  CreateProcess()函数的原型声明如下:

BOOL CreateProcess(
 LPCTSTR lpApplicationName, // 可执行模块名
 LPTSTR lpCommandLine, // 命令行字符串
 LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程的安全属性
 LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程的安全属性
 BOOL bInheritHandles, // 句柄继承标志
 DWORD dwCreationFlags, // 创建标志
 LPVOID lpEnvironment, // 指向新的环境块的指针
 LPCTSTR lpCurrentDirectory, // 指向当前目录名的指针
 LPSTARTUPINFO lpStartupInfo, // 指向启动信息结构的指针
 LPPROCESS_INFORMATION lpProcessInformation // 指向进程信息结构的指针
);

  在程序设计时,某一个具体的功能模块可以通过函数或是线程等不同的形式来实现。对于同一进程而言,这些函数、线程都是存在于同一个地址空间下的,而且在执行时,大多只对与其相关的一些数据进行处理。如果算法存在某种错误,将有可能破坏与其同处一个地址空间的其他一些重要内容,这将造成比较严重的后果。为保护地址空间中的内容可以考虑将那些需要对地址空间中的数据进行访问的操作部分放到另外一个进程的地址空间中运行,并且只允许其访问原进程地址空间中的相关数据。具体的,可在进程中通过CreateProcess()函数去创建一个子进程,子进程在全部处理过程中只对父进程地址空间中的相关数据进行访问,从而可以保护父进程地址空间中与当前子进程执行任务无关的全部数据。对于这种情况,子进程所体现出来的作用同函数和线程比较相似,可以看成是父进程在运行期间的一个过程。为此,需要由父进程来掌握子进程的启动、执行和退出。下面这段代码即展示了此过程:

// 临时变量
CString sCommandLine;
char cWindowsDirectory[MAX_PATH];
char cCommandLine[MAX_PATH];
DWORD dwExitCode;
PROCESS_INFORMATION pi;
STARTUPINFO si = {sizeof(si)};
// 得到Windows目录
GetWindowsDirectory(cWindowsDirectory, MAX_PATH);
// 启动"记事本"程序的命令行
sCommandLine = CString(cWindowsDirectory) + "\\NotePad.exe";
::strcpy(cCommandLine, sCommandLine);
// 启动"记事本"作为子进程
BOOL ret = CreateProcess(NULL, cCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (ret) {
 // 关闭子进程的主线程句柄
 CloseHandle(pi.hThread);
 // 等待子进程的退出
 WaitForSingleObject(pi.hProcess, INFINITE);
 // 获取子进程的退出码
 GetExitCodeProcess(pi.hProcess, &dwExitCode);
 // 关闭子进程句柄
 CloseHandle(pi.hProcess);

}

  此段代码首先通过CreateProcess()创建Windows自带的“记事本”程序为子进程,子进程启动后父进程通过WaitForSingleObject()函数等待其执行的结束,在子进程没有退出前父进程是一直处于阻塞状态的,这里子进程的作用同单线程中的函数类似。一旦子进程退出,WaitForSingleObject()函数所等待的pi.hProcess对象将得到通知,父进程将得以继续,如有必要可以通过GetExitCodeProcess()来获取子进程的退出代码。

  相比而言,更多的情况是父进程在启动完子进程后就再不与其进行任何数据交换和通讯,由其创建的子进程的执行成功与否均与父进程无关。许多大型软件在设计时也多采用了这类思想,将某些功能完全通过独立的应用程序来完成,当需要执行某操作时只要通过主程序启动相应的子进程即可,具体的处理工作均由子进程去完成。这类子进程的创建过程更为简单,例如对于上面那段代码只需去除对子进程句柄pi.hProcess的等待即可:

BOOL ret = CreateProcess(NULL, cCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (ret) {
 // 关闭子进程的主线程句柄
 CloseHandle(pi.hThread);
 // 关闭子进程句柄
 CloseHandle(pi.hProcess);
}

  可以通过dwCreationFlags参数在创建进程时设置子进程的优先级。前面的示例代码在创建子进程时使用的均是默认的优先级,如果要将优先级设置为高,可以修改如下:

BOOL ret = CreateProcess(NULL, cCommandLine, NULL, NULL, FALSE, HIGH_PRIORITY_CLASS, NULL, NULL, &si, &pi);

  如果在进程创建时没有特别设置优先级,可以通过SetPriorityClass()函数来动态设定,该函数需要待操作进程的句柄和优先级标识符作为入口参数,函数原型为:

BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);

  对于前面没有设定优先级的例子代码,可以在子进程启动后由父进程来动态改变其优先级设置:

SetPriorityClass(pi.hProcess, HIGH_PRIORITY_CLASS);

  或是由子进程在其启动后自行改变优先级设置,需要注意的是这时进程句柄应设置为子进程自身的句柄,可通过GetCurrentProcess()函数来获取:

HANDLE hProcess = GetCurrentProcess();
SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS);

  进程的互斥运行

  正常情况下,一个进程的运行一般是不会影响到其他正在运行的进程的。但是对于某些有特殊要求的如以独占方式使用串行口等硬件设备的程序就要求在其进程运行期间不允许其他试图使用此端口设备的程序运行的,而且此类程序通常也不允许运行同一个程序的多个实例。这就引出了进程互斥的问题。

  实现进程互斥的核心思想比较简单:进程在启动时首先检查当前系统是否已经存在有此进程的实例,如果没有,进程将成功创建并设置标识实例已经存在的标记。此后再创建进程时将会通过该标记而知晓其实例已经存在,从而保证进程在系统中只能存在一个实例。具体可以采取内存映射文件、有名事件量、有名互斥量以及全局共享变量等多种方法来实现。下面就分别对其中具有代表性的有名互斥量和全局共享变量这两种方法进行介绍:

// 创建互斥量
HANDLE m_hMutex = CreateMutex(NULL, FALSE, "Sample07");
// 检查错误代码
if (GetLastError() == ERROR_ALREADY_EXISTS) {
 // 如果已有互斥量存在则释放句柄并复位互斥量
 CloseHandle(m_hMutex);
 m_hMutex = NULL;
 // 程序退出
 return FALSE;
}

  上面这段代码演示了有名互斥量在进程互斥中的用法。代码的核心是CreateMutex()对有名互斥量的创建。CreateMutex()函数可用来创建一个有名或无名的互斥量对象,其函数原型为:

HANDLE CreateMutex(
 LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针
 BOOL bInitialOwner, // 初始化互斥对象的所有者
 LPCTSTR lpName // 指向互斥对象名的指针
);

  如果函数成功执行,将返回一个互斥量对象的句柄。如果在CreateMutex()执行前已经存在有相同名字的互斥量,函数将返回这个已经存在互斥量的句柄,并且可以通过GetLastError()得到错误代码ERROR_ALREADY_EXIST。可见,通过对错误代码ERROR_ALREADY_EXIST的检测可以实现CreateMutex()对进程的互斥。

  使用全局共享变量的方法则主要是在MFC框架程序中通过编译器来实现的。通过#pragma data_seg预编译指令创建一个新节,在此节中可用volatile关键字定义一个变量,而且必须对其进行初始化。Volatile关键字指定了变量可以为外部进程访问。最后,为了使该变量能够在进程互斥过程中发挥作用,还要将其设置为共享变量,同时允许具有读、写访问权限。这可以通过#pragma comment预编译指令来通知编译器。下面给出使用了全局变量的进程互斥代码清单:

#pragma data_seg("Shared")
int volatile g_lAppInstance =0;
#pragma data_seg()
#pragma comment(linker,"/section:Shared,RWS")
……
if(++g_lAppInstance>1)
return FALSE;

  此段代码的作用是在进程启动时对全局共享变量g_nAppInstancd 加1 ,如果发现其值大于1,那么就返回FALSE以通知进程结束。这里需要特别指出的是,为了使以上两段代码能够真正起到对进程互斥的作用,必须将其放置在应用程序的入口代码处,即应用程序类的初始化实例函数InitInstance()的开始处。

  结束进程

  进程只是提供了一段地址空间和内核对象,其运行是通过在其地址空间内的主线程来体现的。当主线程的进入点函数返回时,进程也就随之结束。这种进程的终止方式是进程的正常退出,进程中的所有线程资源都能够得到正确的清除。除了这种进程的正常推出方式外,有时还需要在程序中通过代码来强制结束本进程或其他进程的运行。ExitProcess()函数即可在进程中的某个线程中使用,并将立即终止本进程的运行。ExitProcess()函数原型为:

VOID ExitProcess(UINT uExitCode);

  其参数uExitCode为进程设置了退出代码。该函数具有强制性,在执行完毕后进程即已经被结束,因此位于其后的任何代码将不能被执行。虽然ExitProcess()函数可以在结束进程的同时通知与其相关联的动态链接库,但是由于它的这种执行的强制性,使得ExitProcess()函数在使用上将存在有安全隐患。例如,如果在程序调用ExitProcess()函数之前曾用new操作符申请过一段内存,那么将会由于ExitProcess()函数的强制性而无法通过delete操作符将其释放,从而造成内存泄漏。有鉴于ExitProcess()函数的强制性和不安全性,在使用时一定要引起注意。

  ExitProcess()只能强制执行本进程的退出,如果要在一个进程中强制结束其他的进程就要用TerminateProcess()来实现。与ExitProcess()不同,TerminateProcess()函数执行后,被终止的进程是不会得到任何关于程序退出的通知的。也就是说,被终止的进程是无法在结束运行前进行退出前的收尾工作的。所以,通常只有在其他任何方法都无法迫使进程退出时才会考虑使用TerminateProcess()去强制结束进程的。下面给出TerminateProcess()的函数原型:

BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode);

  参数hProcess和uExitCode分别为进程句柄和退出代码。如果被结束的是本进程,可以通过GetCurrentProcess()获取到句柄。TerminateProcess()是异步执行的,在调用返回后并不能确定被终止进程是否已经真的退出,如果调用TerminateProcess()的进程对此细节关心,可以通过WaitForSingleObject()来等待进程的真正结束。

  小结

  多进程是多任务管理中的重要内容,文中上述部分对其基本概念和主要的技术如子进程的创建与结束、进程间的互斥运行等做了较详细的介绍。通过本文读者应能对多进程管理有一个初步的认识。

分享到:
评论

相关推荐

    VC++中进程与多进程管理的方法

    ### VC++中进程与多进程管理的方法 #### 进程概念 进程是在操作系统环境中一个被加载至内存并处于运行状态的应用程序实例。每个进程都由内核对象与地址空间构成,其中内核对象用于存储与进程相关的统计信息,并辅助...

    VC++多线程与多进程编程

    本资料“VC++多线程与多进程编程”专注于利用Microsoft Visual C++(VC++)进行多线程和多进程的实践操作,是初学者掌握这些高级编程概念的理想资源。 首先,我们来理解多线程的概念。线程是操作系统分配处理器时间...

    VC++实现进程管理

    在IT领域,进程管理是操作系统的核心组成部分,它涉及到如何有效地调度和控制计算机系统中的程序执行。在Windows操作系统中,VC++(Visual C++)是一种常用的编程工具,它提供了丰富的库和API来帮助开发者实现进程...

    VC++中进程间相互通信十一种方法

    ### VC++中进程间相互通信的十一种方法 #### 一、进程概念与通信背景 在计算机科学中,进程通常被定义为一个正在运行的程序的实例,它由两部分组成: 1. **操作系统用来管理进程的内核对象**:这部分是系统用来...

    VC++中进程与多进程管理的方法详解

    在VC++编程环境中,进程和多进程管理是实现并发执行和资源隔离的关键技术。进程是操作系统中的基本执行单元,它包含了程序的上下文和状态,包括代码、数据、线程以及系统资源。每个进程都有独立的地址空间,这意味着...

    vc++进程调度代码

    在计算机科学中,进程调度是操作系统中的核心组件之一,它负责管理系统的执行单元——进程,决定哪个进程在何时获得CPU的使用权。在Windows系统中,VC++(Microsoft Visual C++)作为开发工具,可以用于编写实现进程...

    vc++管理进程(附源码)

    总的来说,这个项目是一个实用的工具,可以帮助开发者或系统管理员更方便地管理Windows系统中的进程,提高他们的工作效率。通过学习和理解这段代码,开发者可以深入掌握Windows API在进程管理方面的应用,进一步提升...

    VC++ 系统进程管理器

    进程管理是操作系统核心功能之一,涉及到进程创建、撤销、调度、同步与通信等多个方面。 在VC++环境中,我们可以使用Windows API(应用程序接口)来访问和操作系统的进程管理功能。例如,`CreateProcess`函数用于...

    vc++守护进程守护一个程序的开启,关闭自动打开.

    根据提供的文件信息,我们可以...- **多进程管理**:扩展程序功能以支持同时守护多个目标程序。 综上所述,通过VC++创建守护进程不仅可以实现对特定程序的监控和管理,还可以进一步扩展其功能以满足更复杂的业务需求。

    vc++6.0 网络进程监视器

    综上所述,开发一个VC++6.0网络进程监视器涵盖了C++语言、MFC框架、Windows API、网络编程、多线程、进程和线程管理等多个方面的技术。掌握这些知识,不仅对于创建此类工具,也对于提升整个软件开发能力具有重要意义...

    vc++开发的源代码,系统进程管理器的开发,非常好的学习参考,对程序员很有帮助.

    在实现过程中,我们需要正确地管理和使用这些函数,确保程序的稳定性和安全性。 其次,要实现进程信息的获取,我们需要用到GetProcessTimes、GetProcessMemoryInfo等API,这些函数可以帮助我们获取到进程的运行时间...

    VC++模拟进程分配器

    本篇将详细探讨一个基于VC++实现的模拟进程分配器的设计与实现,这对于我们理解操作系统的工作原理、进程调度算法以及编程实践具有深远意义。 首先,我们要明白什么是进程分配器。在操作系统中,进程是执行中的程序...

    进程调度vc++程序

    进程调度是操作系统中一个至关重要的概念,它负责在多任务环境下有效地分配CPU资源。在这个VC++程序中,包含了三种常见的进程调度算法:先来先服务(FCFS)、时间片轮转(RR)以及优先级调度。下面将详细介绍这些...

    VC++ 进程通信

    命名管道与匿名管道类似,但提供了命名机制,使得任何知道管道名称的进程都可以进行通信,从而支持多对一或一对多的通信模式。通过`CreateNamedPipe`函数创建,使用`ConnectNamedPipe`和`TransactNamedPipe`进行数据...

    VC++ 枚举进程模块信息

    在VC++编程环境中,枚举进程模块是一项重要的技术,它涉及到操作系统级别的进程管理和系统调试。本文将深入探讨如何使用VC++来枚举一个进程中的所有模块信息,以及这些信息在服务管理和系统分析中的应用。 首先,...

    vc++挂起进程 冻结进程

    在Windows编程环境中,VC++(Microsoft Visual C++)是一种强大的集成开发环境,它允许程序员创建各种类型的应用程序,包括控制底层系统行为的功能,如挂起和冻结进程。挂起进程是指将一个正在运行的进程暂时停止...

    VC++中进程间相互通信的十一种方法

    本文将详细解析十一种不同的VC++进程间通信方法。 1. **剪贴板(Clipboard)**:在早期的16位系统中,剪贴板常用于进程间的简单数据交换。VC++的CWnd类提供了对剪贴板操作的支持,包括复制和粘贴数据。 2. **窗口...

    vc++开发的进程管理器ProcessMonitor.zip

    通过以上分析,我们可以看到,开发一个VC++进程管理器涉及到了许多核心的编程技术和操作系统原理。对于初学者来说,这不仅是一个提高编程技能的好项目,也是一个深入了解操作系统内部运作的窗口。同时,对于有经验的...

    vc++6.0操作系统进程调度

    操作系统中的进程调度是系统核心的重要组成部分,它负责管理和控制系统的执行流程,确保多个并发进程能够高效、公平地共享处理器资源。VC++6.0是一种经典的集成开发环境(IDE),主要用于编写C++语言的程序,虽然它...

Global site tag (gtag.js) - Google Analytics