实验一:进程控制

实验一:进程控制

 实验目的

通过在Windows 任务管理器中对进程进行相应的管理操作,熟悉操作系统进程管理的概念,学习观察操作系统运行的动态性能;学习创建进程、观察进程和终止进程的程序设计方法;理解Windows进程的“一生”。

 实验内容

(1)使用任务管理器查看、终止进程;
(2)创建进程
(3)获取进程运行信息,改变进程优先级
(4)终止进程

 实验步骤

1、创建进程

(1)父进程创建子进程,子进程创建下一个子进程;
(2)创建子进程时将子进程序号作为命令行参数传给子进程,子进程将序号加一后继续创建下一个子进程;
(3)限制创建子进程的最大个数;
(4)进程在终止之前暂停一下,输入一个字符后,进程结束,以便观察。

CreateProcess.cpp

//Defines the entry point for the console application
//Defines the entry point for the console application
#include"stdafx.h"
#include<windows.h>
#include<iostream>
#include<stdio.h>
//创建子进程,与父进程执行相同的程序
void StartClone(int nCloneID)
{
    //获取当前进程的可执行文件名

    TCHAR szFilename[MAX_PATH];
    //获取当前应用程序的路径
    ::GetModuleFileName(NULL,szFilename,MAX_PATH);
    /*
        第一个参数:
            模块句柄指向的就是EXE和DLL等在虚拟地址空间的位置。
            如果该参数为NULL,该函数返回该应用程序全路径。

        第二个参数:
            存放返回的名字的内存块的指针,是一个输出参数
        
        第三个参数:
            内存块的大小,用于防止溢出.
    */

    // 格式化用于创建新进程的命令行,包括EXE文件名和克隆ID
    //char Tchar(char,wchar) LPCSTR LPSTR
    TCHAR szCmdLine[MAX_PATH];
    ::sprintf(szCmdLine,"\"%s\"%d",szFilename,nCloneID);

    //用于新进程的STARTUPINFO结构
    STARTUPINFO si;
    ::ZeroMemory(reinterpret_cast<void*>(&si),sizeof(si));
    si.cb =sizeof(si);// 必须是本结构的大小

    // 返回的用于新进程的进程信息
    PROCESS_INFORMATION pi;

    //使用同一可执行文件和带有克隆ID的命令行创建新进程。
    //CreateProcess 是一个在 CreateProcessA 和 CreateProcessW 之间切换的宏,它们分别采用 ANSI 或 Unicode 中的字符串
    /*
        第一个参数:新进程的可执行文件名
        第二个参数:传给新进程的命令行参数
        第三个参数:缺省的进程安全性
        第四个参数:缺省的线程安全性
        第五个参数:不继承句柄
        第六个参数:使用新的控制台
        第七个参数:新的环境CREATE_NEW_CONSOLE,
        第八个参数:当前目录
        第九个参数:启动信息
        第十个参数:返回进行的信息
    */
    BOOL bCreateOK=::CreateProcess(
        szFilename,szCmdLine,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi
    );

    //不使用的句柄最好关掉
    if(bCreateOK)
    {
        ::CloseHandle(pi.hProcess);
        ::CloseHandle(pi.hThread);
    }
}
int main(int argc,char* argv[])
{   
    //进程的克隆ID
    int nClone(0);

    //第一个第一个进程argc为1,以后每个进程命令行有两个参数,
    //其中第二个是克隆ID。
    if (argc>1)
    {
        //从第二个参数中提取克隆
        ::sscanf(argv[1],"%d",&nClone);
    }

    //显示进程的克隆ID
    std::cout<<"Process ID:"<<::GetCurrentProcessId()<<",Clone ID:"<<nClone<<std::endl;

    //创建新进程,其nClone为1-8
    const int c_nCloneMax=8;
	if (nClone==0)
    {
		//argc main函数中参数个数
		for(int i=1;i<=c_nCloneMax;i++)
		{
			StartClone(i);
		}	    
    }

    //等待输入一个字符,以便观察。输入一个字符后,程序结束。
    std::cout<<"input a char:";
    getchar();
    std::cout<<"I'm stopped ... Bye-Bye";
	::Sleep(1000);
    return 0; 
}

控制在一个窗口的检测
在这里插入图片描述
控制每一个进程一个窗口的检测

在这里插入图片描述

思考题

1.运行后在系统任务管理器中查看所创建进程映像名,有什么规律?

映像名一样,与创建顺序相匹配

2.父子进程使用同一程序,如何进行区别?

nClone克隆ID进行区分

3.不同进程的克隆ID是如何获取到的?

//传输:
  :: sprintf(szCmdLine,"\"%s\"%d",szFilename, nCloneID);
//中间阶段:	
  在进程的main函数中取出,   argv[1]
//获取:从第二个参数中提取克隆ID
  :: sscanf(argv[1] , "%d" , &nClone);

4.如果在克隆ID为0的进程中创建克隆ID为1-8的进程,应该如何修改程序?

	if (nClone==0)
    {
		for(int i=1;i<=c_nCloneMax;i++)
		{
			StartClone(i);
		}	    
    }

2、查看进程运行信息

(1) 在main()函数中获取当前进程ID,获取当前进程版本信息;
(2) 获取操作系统版本信息;
(3) 获取当前进程优先级;
(4) 如果操作系统版本>=Windows XP,并且当前进程优先级不是HIGH_PRIORITY_CLASS,将当前进程优先级改为HIGH_PRIORITY_CLASS;
(5) 显示改变后的进程优先级。

ViewPRI.cpp

// ViewPRI.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <windows.h>
#include <iostream>
// 显示进程和操作系统信息的简单示例
int main(int argc, char* argv[])
{
    // 获取这个进程的ID号
    DWORD dwIdThis=:: GetCurrentProcessId();
    
	// 获得和显示这一进程所需的版本,也可以用0表示当前进程
    DWORD dwVerReq=:: GetProcessVersion(dwIdThis);
    WORD wMajorReq=(WORD)(dwVerReq>>16) ;
    WORD wMinorReq=(WORD)(dwVerReq & 0xffff) ;
    std :: cout << "Process ID: "<< dwIdThis
                   <<", requires OS: " << wMajorReq <<"." 
                   << wMinorReq << std :: endl ;
    
	// 设置版本信息的数据结构,以便保存操作系统的版本信息
    OSVERSIONINFOEX osvix;
    :: ZeroMemory(&osvix, sizeof(osvix) ) ;
    osvix.dwOSVersionInfoSize=sizeof(osvix) ;
   
	// 获取版本信息和显示
    :: GetVersionEx(reinterpret_cast < LPOSVERSIONINFO > (&osvix)) ;
    std :: cout << "Running on OS:" << osvix.dwMajorVersion <<"."
                << osvix.dwMinorVersion << std :: endl;
    
	
	//显示当前进程的优先级。
	//GetCurrentProcess()返回当前进程的句柄,事实上这个函数目前只是简单的返回-1这个值。
	//但是为了兼容性考虑,我们最好不要直接使用-1,因为并不能保证以后这个值不会改变。
	DWORD dwProcessP=::GetPriorityClass(GetCurrentProcess());
 	std::cout<<"Current process priority is :";
	    switch(dwProcessP) 
		{
            case HIGH_PRIORITY_CLASS:
			     std::cout<<"High";
			     break;
            case NORMAL_PRIORITY_CLASS:
			     std::cout<<"Normal";
			     break;
            case IDLE_PRIORITY_CLASS:
			     std::cout<<"Idle";
		     	 break;
            case REALTIME_PRIORITY_CLASS:
			     std::cout<<"Realtime";
			     break;
            default:
			     std::cout<<"<unknow>";
			     break;
		}
	    std::cout<<std::endl;
    
      // 如果是NTS(Windows 2000) 系统,则提高其优先权
      if (osvix.dwPlatformId==VER_PLATFORM_WIN32_NT && osvix.dwMajorVersion >= 5)
      {
        if(dwProcessP!=HIGH_PRIORITY_CLASS)
		{
			// 如果当前优先级不是high,则改变优先级为high
			:: SetPriorityClass(
			:: GetCurrentProcess() ,			
			HIGH_PRIORITY_CLASS);
    
			//报告给用户
			DWORD dwProcessP = GetPriorityClass(GetCurrentProcess());
			std::cout<<"The Process priority have been changed to ";
			switch(dwProcessP) 
			{
				 case HIGH_PRIORITY_CLASS:
					 std::cout<<"High";
					 break;
				 case NORMAL_PRIORITY_CLASS:
					 std::cout<<"Normal";
					 break;
				 case IDLE_PRIORITY_CLASS:
					 std::cout<<"Idle";
		     		 break;
				 case REALTIME_PRIORITY_CLASS:
					 std::cout<<"Realtime";
					 break;
				 default:
					 std::cout<<"<unknow>";
					 break;
			}
			std::cout<<std::endl;
		}
	}
	return 0;
}

在这里插入图片描述

思考题

1.进程的优先级存放在哪里?
PCB(进程控制块)中

//获取当前进程的优先级
GetPriorityClass(GetCurrentProcess())

3、终止进程

(1) 在程序一的基础上,当前进程每创建一个新进程,增加一个参数,用以表示当前进程的进程ID;
(2) 新进程运行时获取父进程的进程ID;
(3) 在指定的进程中获取父进程的句柄;
(4) 在指定的进程中终止父进程。

TerminateProcess

// TerminateProcess.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <stdio.h>

//创建子进程,与父进程执行相同的程序。
void StartClone(int nCloneID)
{
	
    // 获取当前进程的可执行文件名取当前进程的可执行文件名
    TCHAR szFilename[MAX_PATH] ;
    :: GetModuleFileName(
		          NULL, //一个模块的句柄。模块句柄跟一般的句柄不一样,
				        //模块句柄指向的就是EXE和DLL等在虚拟地址空间的位置。
                        //如果该参数为NULL,该函数返回该应用程序全路径。
				  szFilename,
				  MAX_PATH) ;
   
    // 格式化用于创建新进程的命令行,包括EXE文件名、克隆ID和当前进程的ID。
    TCHAR szCmdLine[MAX_PATH];
	:: sprintf(szCmdLine,"\"%s\"%d %d",szFilename,nCloneID,::GetCurrentProcessId());
	  
	// 用于新进程的STARTUPINFO结构
    STARTUPINFO si;
    :: ZeroMemory(reinterpret_cast <void*> (&si) , sizeof(si) ) ;
    si.cb = sizeof(si) ;				// 必须是本结构的大小
    // 返回的用于新进程的进程信息
    PROCESS_INFORMATION pi;
    
    // 使用同一可执行文件和带有克隆ID、当前进程ID的命令行创建新进程。
    BOOL bCreateOK=::CreateProcess(
        szFilename,					// 新进程的可执行文件名
        szCmdLine,					// 传给新进程的命令行参数
        NULL,						// 缺省的进程安全性
        NULL,						// 缺省的线程安全性
        FALSE,					  	// 不继承句柄
        CREATE_NEW_CONSOLE,	        // 使用新的控制台
        NULL,						// 新的环境
        NULL,						// 当前目录
        &si,						// 启动信息
        &pi) ;					    // 返回的进程信息
   
    //不使用的句柄最好关掉
    if (bCreateOK)
    {
        :: CloseHandle(pi.hProcess) ;
        :: CloseHandle(pi.hThread) ;
    }
}

//某个子进程删除创建它的父进程。
int main(int argc, char* argv[] )
{
    // 进程的克隆ID
    int nClone(0) ;
    // 保存父进程的进程ID
	int ProcessId(0);
    
    //第一个进程argc为1,以后每个进程命令行有3个参数,
    //其中第3个是父进程ID。
    if (argc > 1)
    {
        // 从第2个参数中提取克隆ID
        :: sscanf(argv[1] , "%d" , &nClone) ;

		// 从第3个参数中提取父进程ID
        :: sscanf(argv[2] , "%d" , &ProcessId) ;

    }
    
    // 显示进程ID和克隆ID
    std :: cout << "Process ID:" << :: GetCurrentProcessId()
                << ", Clone ID:" << nClone
                << std :: endl;

	//终止父进程
	if(nClone==3)
	{
		::TerminateProcess(OpenProcess(PROCESS_TERMINATE,FALSE,ProcessId),1);
		std::cout<<"Terminate Process  Clone ID=2"
			     << std :: endl;
	}

    
    // 创建新进程,其nClone为1-8
    const int c_nCloneMax=8;
    if (nClone < c_nCloneMax)
    {
		:: Sleep(1000) ;
        StartClone(nClone+1) ;
    }
    
    // 等待输入一个字符,以便观察。输入一个字符后,程序结束。
    std::cout<<"input a char:";
    getchar();
    std::cout<<"I'm stopped ... Bye-Bye";
    :: Sleep(1000) ;
    return 0;
}

在这里插入图片描述
在这里插入图片描述

思考题

1.一个进程终止其父进程所需的句柄是如何获取的?

先从第3个参数中提取父进程ID
::sscanf(argv[2],%d”,&processId)
然后通过父进程ID调用OpenProcess获取句柄,
OpenProcess(PROCESS_TERMINATE,FALSE,ProcessId)

2.Windows下的进程ID和进程句柄有什么区别?

进程id是进程唯一标识符,通用、公开,因此,可以在线程\进程之间共享。
进程句柄实际是一个指针的指针,指向一地包含具体信息数据的内存,每次打开这个程序(OpenProcess)返回的句柄是变化的。
一个进程在不同的调用时间中可能句柄值是不同的,但是ID只能有一个,且进程句柄只在当前进程内有效

3.试通过修改程序来说明对一个进程OpenProcess()多次,返回的句柄值是否相同?

不同,
因为当前进程结束的是上一进程,
句柄是指指针数组的索引号,打开多次访问的是同一个文件,
不同的句柄显示不同的值,但本质上是相同的。

通过在进程nClone为了进程中对父进程调用三次OpenProcess
for(int i=0;i<3;i++)
{
	HANDLE hProcess= OpenProcess(PROCESS_TERMINATE,FALSE,processId);
	std::cout<<"TEST:"<<hProcess<<std::endl;
}
所打印内容为:TEST:00000088
              TEST:000000D0
              TEST:000000D4
所以得到结论:对一个进程OpenProcess多次返回的句柄不同。


句柄(Handle) (指针的指针)
    是一个是用来标识对象或者项目的标识符,可以用来描述窗体、文件等,值得注意的是句柄不能是常量 。
    Windows之所以要设立句柄,根本上源于内存管理机制的问题,即虚拟地址。
    简而言之数据的地址需要变动,变动以后就需要有人来记录、管理变动,因此系统用 句柄来记载数据地址的变更。
    在程序设计中,句柄是一种特殊的智能指针,当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄

  • 11
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验目的: 1. 理解进程的概念与特点; 2. 掌握进程创建、撤销和阻塞唤醒的实现方法; 3. 掌握进程调度算法的实现; 4. 掌握进程同步与互斥的实现方法。 实验内容: 1. 进程控制块(PCB)的定义 进程控制块是操作系统的一种数据结构,用于描述进程的基本信息和运行状态。它包含了进程的标识符、程序计数器、寄存器、内存分配情况、打开文件列表、进程优先级、进程状态等信息。PCB 是操作系统内核的重要组成部分,是操作系统实现进程管理和调度的关键数据结构。 2. 进程创建、撤销和阻塞唤醒 进程创建是指在操作系统新建一个进程并为其分配资源的过程。进程的撤销是指终止一个正在运行的进程并回收其占用的资源。进程的阻塞是指在执行过程暂停进程的执行,等待某种条件满足后再继续执行。进程的唤醒是指在进程阻塞期间,当等待的条件满足后,操作系统会将进程重新唤醒并恢复其执行。 3. 进程调度算法 进程调度算法是指操作系统用于决定哪个进程能够占用 CPU 资源的算法。常见的进程调度算法有先来先服务(FCFS)、短作业优先(SJF)、时间片轮转(RR)、优先级调度等。 4. 进程同步与互斥 进程同步与互斥是指在多进程环境下,为了保证进程之间的合作和协调,需要采用一些机制来保证进程之间的正确性和互不干扰。常用的进程同步与互斥方法有信号量、互斥锁、条件变量等。 实验要求: 1. 完成进程创建、撤销和阻塞唤醒的实现; 2. 实现先来先服务(FCFS)和时间片轮转(RR)两种进程调度算法; 3. 实现基于信号量的进程同步与互斥。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值