本文实例讲述了VC++中进程与多进程管理的方法,分享给大家供大家参考。具体方法分析如下:
摘要: 本文主要介绍了多任务管理中的多进程管理技术,对进程的互斥运行、子进程的创建与结束等作了较详细的阐述。
关键词: 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 // 指向进程信息结构的指针 );
// 临时变量 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); }
BOOL ret = CreateProcess(NULL, cCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); if (ret) { // 关闭子进程的主线程句柄 CloseHandle(pi.hThread); // 关闭子进程句柄 CloseHandle(pi.hProcess); }
BOOL ret = CreateProcess(NULL, cCommandLine, NULL, NULL, FALSE, HIGH_PRIORITY_CLASS, NULL, NULL, &si, &pi);
BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);
SetPriorityClass(pi.hProcess, HIGH_PRIORITY_CLASS);
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; }
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针 BOOL bInitialOwner, // 初始化互斥对象的所有者 LPCTSTR lpName // 指向互斥对象名的指针 );
使用全局共享变量的方法则主要是在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;
结束进程
进程只是提供了一段地址空间和内核对象,其运行是通过在其地址空间内的主线程来体现的。当主线程的进入点函数返回时,进程也就随之结束。这种进程的终止方式是进程的正常退出,进程中的所有线程资源都能够得到正确的清除。除了这种进程的正常推出方式外,有时还需要在程序中通过代码来强制结束本进程或其他进程的运行。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);
小结
多进程是多任务管理中的重要内容,文中上述部分对其基本概念和主要的技术如子进程的创建与结束、进程间的互斥运行等做了较详细的介绍。通过本文读者应能对多进程管理有一个初步的认识。
希望本文所述对大家的VC++程序设计有所帮助。
本文向大家介绍VC下通过系统快照实现进程管理的方法,包括了VC下通过系统快照实现进程管理的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了VC下通过系统快照实现进程管理的方法,分享给大家供大家参考。具体实现方法如下: 一、引言 每一个应用程序实例在运行起来后都会在当前系统下产生一个进程,大多数应用程序均拥有可视界面,用户可以通过标题栏上的关闭按钮关闭程序。但是也有为数不少的在后台运
NodeJS可以感知和控制自身进程的运行环境和状态,也可以创建子进程并与其协同工作,这使得NodeJS可以把多个程序组合在一起共同完成某项工作,并在其中充当胶水和调度器的作用。本章除了介绍与之相关的NodeJS内置模块外,还会重点介绍典型的使用场景。 开门红 我们已经知道了NodeJS自带的fs模块比较基础,把一个目录里的所有文件和子目录都拷贝到另一个目录里需要写不少代码。另外我们也知道,终端下的
如果项目依赖各种外界的进程,应使用 foreman 来管理它们。
细节 进程一般分为批处理进程、交互进程和守护进程三类。 守护进程总是活跃,在系统启动时通过脚本自动启动,或由 root 启动,通常在后台运行。 一个进程可以拥有子进程。当父进程终止时,它的子进程也随之终止;而子进程终止时,父进程通常可以继续运行。 init 进程为根进程,所有进程都是它的子进程 ps 显示进程信息,选项可省略 “-” aux 以 BSD风格 显示进程 常用 -efH 以 Syste
PROCESS MANAGEMENT 在任何时间,Linux 系统通常同时运行数百个,甚至数千个进程。进程只是一个正在运行和使用资源的程序。它包括终端,Web 服务器,任何正在运行的命令,任何数据库,GUI 界面等等。任何优秀的 Linux 管理员(特别是黑客)都需要了解如何管理他们的流程以优化他们的系统。例如,一旦黑客控制了目标系统,他们可能想要找到并停止某个进程,如防病毒应用程序或防火墙。为此
ps 命令查看进程 ps 用来显示操作系统上的进程,包括进程 UID,PID,PPID,CPU 使用情况等。ps aux $ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.3 128176 6860 ? Ss 12:36