进程的控制和通信(Windows2000)
一、实验目的
通过对Windows 2000编程,进一步熟悉操作系统的基本概念,并能较好地理解Windows 2000的结构。
通过创建进程,观察进程的运行和终止程序以及调试操作,进一步熟悉操作系统的进程概念,理解Windows 2000进程的生命周期。
二、实验要求与内容、过程与结果
- 参照例2-2,编写一个程序,利用进程句柄来获得当前运行进程的优先级。
#include<Windows.h> #include<iostream> using namespace std; int main() { //从当前进程中提取句柄 HANDLE hProcessThis=GetCurrentProcess(); //请求内核提供该进程所属的优先权类 DWORD dwPriority=GetPriorityClass(hProcessThis); //发出消息,为用户描述该类 printf("current process priority:"); switch(dwPriority) { case HIGH_PRIORITY_CLASS: printf("High\n"); break; case NORMAL_PRIORITY_CLASS: printf("Normal\n"); break; case IDLE_PRIORITY_CLASS: printf("Idle\n"); break; //case REAL_TIME_ PRIORITY_CLASS: // printf("Realtime\n"); // break; default: printf("<unknow>\n"); break; } }
程序分析
本代码主要通过Windows API函数GetPriorityClass获取当前进程的优先级类别,然后通过switch语句输出对应的优先级名称。
在Windows操作系统下,为了更好地管理和调度进程,操作系统将所有的进程分成了不同的优先级类别,这些类别按照优先级从高到低依次为:
- REALTIME_PRIORITY_CLASS:实时优先级类,只有操作系统内核可以使用,在执行实时任务时使用。
- HIGH_PRIORITY_CLASS:高优先级类,通常用于非常紧急且时间敏感的任务,如音视频编解码、实时数据传输等。
- NORMAL_PRIORITY_CLASS:普通优先级类,大多数进程都属于此类别,系统分配的默认类别。
- IDLE_PRIORITY_CLASS:空闲优先级类,用于低优先级的任务,不会影响其他进程的执行,通常用于节能和减少电脑噪音等场景。
在本代码中,首先调用GetPriorityClass函数获取当前进程的优先级类别,返回的是DWORD类型的优先级常量,然后通过switch语句将其转化为对应的字符串输出。
需要注意的是,在代码中我们注释掉了REALTIME_PRIORITY_CLASS这个优先级类别,因为该优先级类别只有操作系统内核才能使用,一般用户进程无法使用,否则可能会对系统稳定性造成危害。实际上,该优先级类别在一些特定场景下还是会被用到的,如视频监控、游戏引擎等。
总之,通过获取和了解进程的优先级类别,我们可以更好地控制进程的执行顺序和优先级,从而提升系统性能和响应速度。
- 运行例2-3,记录运行结果(不少于5个进程的信息),并描述该程序的功能。
程序功能:利用进程句柄获得进程详细信息, 应用程序显示当前运行进程名字以及在核心态下占用时间的百分比。
- 运行例2-4,记录运行结果,并描述该程序的功能。
程序功能:通过当前进程,创建相同的子进程。
4.参照例2-5,编写一个程序,给出运行进程的操作系统的版本号。
5.用命令行的方式运行2次例2-6(每次命令行参数不同:命令行中是否包含“child”参数),记录运行结果,并描述该程序的功能。
功能:创建子进程,描述了进程间的互斥与同步。该程序开始时时先进入parent()函数,然后parent()函数会创建自杀互斥体和一个子进程,并暂停5秒后杀死自己,这时子进程会进入child()函数并打开自杀互斥体,等待父进程杀掉自身后终止。
6.参照例2-6,编写一个程序,通过创建进程来执行一个命令列表(命令列表中有3个应用程序的程序名,这些程序运行一段时间后被中止退出),并输出每个进程(这3个应用程序)的执行时间。
#include <windows.h> #include <iostream> #include <stdio.h> #include<ctime> #pragma warning(disable: 4996) using namespace std; static LPCTSTR g_szMutexName = "w2kdg.ProcTerm.mutex.Suicide"; //创建当前进程的克隆进程的简单方法 void StartClone(int num) { TCHAR szFilename[MAX_PATH]; //获得当前可执行文件的文件名 GetModuleFileName(NULL, szFilename, MAX_PATH); //格式化子进程的命令行,指明是一个EXE文件和子进程 TCHAR szCmdLine[MAX_PATH]; sprintf(szCmdLine, "%s %d", szFilename,num); //子进程的启动信息结构 STARTUPINFO si; ZeroMemory(reinterpret_cast <void *> (&si), sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION p; //说明一个返回的子进程信息结构 //利用同样的可执行文件名和命令行创建进程,并指明是一个子进程 BOOL bCreateOK = CreateProcess( szFilename, //产生的应用程序的名称 szCmdLine, //指定创建一个子进程的符号标识 NULL, //默认的进程安全性 NULL, //默认的线程安全性 FALSE, //不继承句柄 CREATE_NEW_PROCESS_GROUP, //创建新窗口,使输出更直观 NULL, //新环境 NULL, //当前目录 &si, //启动信息结构 &p); //返回的进程信息 //关闭子进程 if (bCreateOK) { CloseHandle(p.hProcess); CloseHandle(p.hThread); } } void Parent() { //创建“自杀”互斥体 HANDLE hMutexSuicide = CreateMutex( NULL, //默认的安全性 TRUE, //创建者是互斥体的最初拥有者 g_szMutexName); //为互斥体命名 if (hMutexSuicide != NULL) { StartClone(1); //创建两个子进程 StartClone(2); Sleep(5000); //暂停(5秒) ReleaseMutex(hMutexSuicide); //通知子进程”杀”掉自身 CloseHandle(hMutexSuicide); //消除互斥体句柄 } printf("name: parent time: %f s\n", (double)clock() / CLOCKS_PER_SEC); getchar(); } void Child(char * argv[]) { //打开“自杀”互斥体 HANDLE hMutexSuicide = OpenMutex( SYNCHRONIZE, //打开用于同步 FALSE, //不需要向下传递 g_szMutexName); //互斥体名称 if (hMutexSuicide != NULL) { WaitForSingleObject(hMutexSuicide, INFINITE); //等待子进程释放互斥体 //准备好终止,清除句柄 CloseHandle(hMutexSuicide); } printf("name: child%s time: %f s\n", argv[1], (double)clock() / CLOCKS_PER_SEC); } int main(int argc, char * argv[]) { //决定其行为是父进程还是子进程 if (argc > 1) { Child(argv); } else { Parent(); } return 0; }
程序分析
本代码主要演示了如何通过创建“克隆”进程的方式实现父进程通知子进程自杀的功能。
该代码的核心是利用Windows API函数CreateProcess创建进程,并利用命令行参数的方式来区分父进程和子进程。父进程会创建两个子进程,然后暂停5秒,并通过创建一个“自杀”互斥体来通知子进程自杀。子进程则会打开这个互斥体并等待通知,收到通知后即可终止自身。
具体实现过程如下:
首先创建了一个名为“w2kdg.ProcTerm.mutex.Suicide”的互斥体,用于父进程通知子进程自杀。
然后在主函数中通过判断命令行参数的个数,来决定当前进程是父进程还是子进程。如果命令行参数个数大于1,则说明当前进程是子进程;否则,就是父进程。可以通过命令行参数的方式把当前进程的角色传递给子进程。
在父进程中,调用StartClone函数创建两个子进程,同时暂停5秒钟。在创建子进程的时候,使用了CREATE_NEW_PROCESS_GROUP参数,这样可以在屏幕上创建两个独立的命令行窗口,使输出更直观。
然后,父进程获得了“自杀”互斥体的句柄,并释放了这个互斥体,通知子进程自杀。父进程还输出了当前运行时间,再通过getchar来等待用户输入,以保证在结束进程之前不会立即关闭命令行窗口。
在子进程中,首先打开“自杀”互斥体,并等待父进程通知。一旦收到通知,子进程就会终止自身,释放句柄并退出。
需要注意的是,这种通过创建“克隆”进程的方式来通信和控制进程的方法并不是一种很好的做法。因为这种方式存在许多潜在的问题,比如进程之间的同步和通信机制都需要手动实现,并且容易导致死锁和进程的长时间挂起等问题。在实际的开发中,建议使用更为高级和稳定的通信和同步机制,如线程、信号量和管道等。