操作系统课设之Windows 进程管理

前言

课程设计开始了,实验很有意思,写博客总结学到的知识
白嫖容易,创作不易,学到东西才是真
本文原创,创作不易,转载请注明!!!
本文链接
个人博客:https://ronglin.fun/archives/171
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/118308142

为了美观,实验源代码在结尾处,整合版见下
链接:https://pan.baidu.com/s/1rXj1QJGuw-BVc5sQWret9w
提取码:Lin2
操作系统课程设计源代码
本次操作系统课程设计合集
操作系统课设之Windows 进程管理
操作系统课设之Linux 进程管理
操作系统课设之Linux 进程间通信
操作系统课设之Windows 的互斥与同步
操作系统课设之内存管理
操作系统课设之虚拟内存页面置换算法的模拟与实现
操作系统课设之基于信号量机制的并发程序设计
操作系统课设之简单 shell 命令行解释器的设计与实现
仅用于学习,如有侵权,请联系我删除

实验题目

Windows 进程管理

实验目的

(1)学会使用 VC 编写基本的 Win32 Consol Application(控制台应用程序)。
(2)通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉操
作系统的进程概念,理解 Windows 进程的“一生”。
(3)通过阅读和分析实验程序,学习创建进程、观察进程、终止进程以及父子进程同步的基本程序设计方法。

实验内容

由于个人习惯,对于C/C++代码,我更喜欢用CodeBlocks开发,所以本次验证性实验和设计类均用CodeBlocks完成。

1-1 编写基本的 Win32 Consol Application

这个实验的主要目的是熟悉编写C/C++控制台程序,对于创建一个C/C++控制台程序,我常用的有两种方式,逐一说明其优缺点。
第一种方式是在菜单栏点击 File – New – File… ,在弹出的窗口中选择 C/C++ source ,然后选择代码类型是C还是C++,之后设置文件保存的位置,然后就会生成一个.c(.cpp)文件,在这个文件中就可以编写c(c++)代码了。编写完成后在菜单栏 点击 Build – Build and run 或者直接按F9,即可查看输出。这种方法创建C/C++控制台程序,优点是轻便,文件最后只生成三个,源代码.c文件,.o文件还有exe文件,缺点是不支持调试。适合轻量级开发
第二种方法是新建一个工程,在菜单栏点击 File – New – Project…,弹出的界面选择 Console Application ,然后选择代码类型和文件保存目录,之后它会生成一个该项目的文件夹,然后有一个main.c(main.cpp)文件,在这个文件中编写代码。这个项目中的核心文件是.cbp,还会生成很多从属文件,优点是调试方便,可以用Debug单步调试,缺点是生成文件多,适合大型项目开发。
本次操作系统课程设计均是用第二种方式生成C++项目编写。

1-2 创建进程

1.创建一个C++项目,将指导书中的1-2代码复制到main.cpp文件中,然后编译运行
2.按照代码中的注释提示修改代码,看有什么不同,共有两处修改
3.通过查阅相关资料分析为何会出现这样的结果

关键代码

    BOOL bCreateOK=::CreateProcess(
                       szFilename, // 产生这个 EXE 的应用程序的名称
                       szCmdLine, // 告诉其行为像一个子进程的标志
                       NULL, // 缺省的进程安全性
                       NULL, // 缺省的线程安全性
                       FALSE, // 不继承句柄
                       CREATE_NEW_CONSOLE, // 使用新的控制台
                       NULL, // 新的环境
                       NULL, // 当前目录
                       &si, // 启动信息
                       &pi) ; // 返回的进程信息

1-3 父子进程的简单通信及终止进程

1.创建一个C++项目,将指导书中的1-3代码复制到main.cpp文件中,然后按F9查看运行结果
2.根据注释修改代码,查看结果
3.查阅资料,分析原因

关键代码

// 决定其行为是父进程还是子进程
    if (argc>1 && :: strcmp(argv[1], "child" ) == 0)
    {
        Child() ;
    }
    else
    {
        Parent() ;
    }

实验结果与分析

1-1 编写基本的 Win32 Consol Application

运行结果:
在这里插入图片描述
return是返回值,如果越界就会返回一个数,后边execution time 是 C/C++控制台程序执行的时间。

1-2 创建进程

不修改的运行结果
在这里插入图片描述
会生成5个子进程,且0号进程结束后其余进程不消失
第一次修改
nClone=0;
结果和原代码一样
第二次修改

if (argc > 1)
 {
// 从第二个参数中提取克隆 ID
        :: sscanf(argv[1], "%d", &nClone) ;
}
//第二次修改:nClone=0;
nClone = 0;

在这里插入图片描述
结果无限生成子进程。

结果分析:
生成子进程的过程是父进程生成一个子进程,然后子进程再“克隆”自己生成一个子进程,如此反复,直到到达代码中规定的 const int c_nCloneMax=5;为止。
在这里插入图片描述
重点在于这三句代码,当父进程生成子进程的时候,会给子进程传递参数,传参的内容就是 代码中的 szCmdLine 字符串,例如0号主进程会给子进程传递2个参数,第一个参数是 szFilename 是从主进程信息中获取,第二个参数就是 nClone,用来控制生成的克隆数量。
分析两次修改
第一次修改是在

if (argc > 1)
{ :: sscanf(argv[1], "%d", &nClone) ;}

之前,执行nClone=0;将nClone的量置为0,但是之后会判断参数数量,如果传进来大于一个参数,就会把传进来的第二个参数赋值给nClone,也就说子进程的nClone最后的值是它的父进程传进来的值,第一次修改的nClone=0;会被sscanf()覆盖掉。
而第二次修改则是在if判断之后,if判断中将父进程传进来的参数赋值给nClone,然后第二次修改则会覆盖掉if中的赋值,将nClone置为0,所以说,nClone的值最后就是0,比较nClone < c_nCloneMax会恒成立,然后就会无限生成子进程,同时子进程的nClone参数永远都是1

1-3 父子进程的简单通信及终止进程

在这里插入图片描述
然后在父进程中输入回车,主进程结束,同时子进程也消失。
修改代码
修改代码之后,子进程不出现,只有主进程
结果分析:
修改了WaitForSingleObject(hMutexSuicide,INFINITE) ;这句代码的第二个参数,将INFINITE改成了0,WaitForSingleObject()函数的第二参数是等待时间,如果超过这个时间(ms),不管互斥体是否可用都继续运行。而INFINITE在C++表示的意思是正无穷,实际上是一个宏定义 #define INFINITE 0xFFFFFFFF,意思是表示一个非常大的数,所以子程序会一直等互斥体可用,然后再结束。
第二次修改
在这里插入图片描述
这个步骤原理和1-2类似,都是识别命令行参数,不再赘述

小结与心得体会

本验证性实验熟悉了CreateProcess()函数的大概用法,同时理解了控制台程序传参的过程。通过简单的修改nClone的值,实现完全不同的效果,同时再次认识到代码设计过程中,要多多注意逻辑错误.同时了解了互斥程序体的使用,利用WaitForSingleObject()和ReleaseMutex()实现互斥操作。收获良多。=w=

源代码

1-1 编写基本的 Win32 Consol Application

#include <iostream>
using namespace std;

int main()
{
    cout << "Hello, Win32 Consol Application" << endl;
    return 0;
}

1-2 创建进程

#include <windows.h>
#include <iostream>
#include <cstdio>
// 创建传递过来的进程的克隆过程并赋于其 ID 值
void StartClone(int nCloneID)
{
// 提取用于当前可执行文件的文件名
    TCHAR szFilename[MAX_PATH] ; /*字符串类型*/

//第一个参数为句柄,NULL则指向当前程序。第二个参数用于存放地址的指针,第三个参数,系统自带的宏定义。不用管.
    GetModuleFileName(NULL, szFilename, MAX_PATH);
// 格式化用于子进程的命令行并通知其 EXE 文件名和克隆 ID
    TCHAR szCmdLine[MAX_PATH];
    sprintf(szCmdLine,"\"%s\" %d",szFilename,nCloneID);
// 用于子进程的 STARTUPINFO 结构
    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si) ) ; /*用0填充*/
    /*包含STARTUPINFO结构中的字节数.如果Microsoft将来扩展该结构,它可用作版本控制手段,应用程序必须将cb初始化为sizeof(STARTUPINFO)*/
    si.cb = sizeof(si) ; // 必须是本结构的大小5
// 返回的用于子进程的进程信息
    PROCESS_INFORMATION pi;
// 利用同样的可执行文件和命令行创建进程,并赋于其子进程的性质
    BOOL bCreateOK=::CreateProcess(
                       szFilename, // 产生这个 EXE 的应用程序的名称
                       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[] )
{
// 确定派生出几个进程,及派生进程在进程列表中的位置
    int nClone=0;
//修改语句:int nClone;
//第一次修改:nClone=0;
    //nClone=0;
    if (argc > 1)
    {
// 从第二个参数中提取克隆 ID
        :: sscanf(argv[1], "%d", &nClone) ;
    }
//第二次修改:nClone=0;
/*此处修改无限生成子进程*/
    //nClone = 0;
// 显示进程位置
    std :: cout << "Process ID:" << :: GetCurrentProcessId()
                << ", Clone ID:" << nClone
                << std :: endl;
// 检查是否有创建子进程的需要
    const int c_nCloneMax=5;
    if (nClone < c_nCloneMax)
    {
// 发送新进程的命令行和克隆号
        StartClone(++nClone) ;
    }
// 等待响应键盘输入结束进程
    getchar();

    return 0;
}

1-3 父子进程的简单通信及终止进程

# include <windows.h>
# include <iostream>
# include <cstdio>
static LPCTSTR g_szMutexName = "w2kdg.ProcTerm.mutex.Suicide" ;
// 创建当前进程的克隆进程的简单方法
void StartClone()
{
// 提取当前可执行文件的文件名
    TCHAR szFilename[MAX_PATH] ;
    GetModuleFileName(NULL, szFilename, MAX_PATH) ;
// 格式化用于子进程的命令行,字符串“child”将作为形参传递给子进程的 main 函数
    TCHAR szCmdLine[MAX_PATH] ;
//实验 1-3 步骤 3:将下句中的字符串 child 改为别的字符串,重新编译执行,执行前请先保存已经
    //完成的工作
    sprintf(szCmdLine, "\"%s\" child", szFilename) ;
// 子进程的启动信息结构
    STARTUPINFO si;
    ZeroMemory(&si,sizeof(si)) ;
    si.cb = sizeof(si) ; // 应当是此结构的大小
// 返回的用于子进程的进程信息
    PROCESS_INFORMATION pi;
// 用同样的可执行文件名和命令行创建进程,并指明它是一个子进程
    BOOL bCreateOK=CreateProcess(
                       szFilename, // 产生的应用程序的名称 (本 EXE 文件)
                       szCmdLine, // 告诉我们这是一个子进程的标志
                       NULL, // 用于进程的缺省的安全性
                       NULL, // 用于线程的缺省安全性
                       FALSE, // 不继承句柄
                       CREATE_NEW_CONSOLE, //创建新窗口
                       NULL, // 新环境
                       NULL, // 当前目录
                       &si, // 启动信息结构
                       &pi ) ; // 返回的进程信息
// 释放指向子进程的引用
    if (bCreateOK)
    {
        CloseHandle(pi.hProcess) ;
        CloseHandle(pi.hThread) ;
    }
}
void Parent()
{
// 创建“自杀”互斥程序体
    HANDLE hMutexSuicide=CreateMutex(
                             NULL, // 缺省的安全性
                             TRUE, // 最初拥有的7
                             g_szMutexName) ; // 互斥体名称
    if (hMutexSuicide != NULL)
    {
// 创建子进程
        std :: cout << "Creating the child process." << std :: endl;
        StartClone() ;
// 指令子进程“杀”掉自身
        std :: cout << "Telling the child process to quit. "<< std :: endl;
//等待父进程的键盘响应
        getchar() ;
//释放互斥体的所有权,这个信号会发送给子进程的 WaitForSingleObject 过程
        ReleaseMutex(hMutexSuicide) ;
// 消除句柄
        CloseHandle(hMutexSuicide) ;
    }
}
void Child()
{
// 打开“自杀”互斥体
    HANDLE hMutexSuicide = OpenMutex(
                               SYNCHRONIZE, // 打开用于同步
                               FALSE, // 不需要向下传递
                               g_szMutexName) ; // 名称
    if (hMutexSuicide != NULL)
    {
// 报告我们正在等待指令
        std :: cout <<"Child waiting for suicide instructions. " << std :: endl;

//子进程进入阻塞状态,等待父进程通过互斥体发来的信号
        /*INFINITE表示最大,参数表为对象句柄和毫秒数*/
        WaitForSingleObject(hMutexSuicide,INFINITE) ;
        //WaitForSingleObject(hMutexSuicide,0);
        //WaitForSingleObject(hMutexSuicide,5000);
//实验 1-3 步骤 4:将上句改为 WaitForSingleObject(hMutexSuicide, 0) ,重新编译执行
// 准备好终止,清除句柄
        std :: cout << "Child quiting." << std :: endl;
        CloseHandle(hMutexSuicide) ;
    }
}
int main(int argc, char* argv[] )
{
// 决定其行为是父进程还是子进程
    if (argc>1 && :: strcmp(argv[1], "child" ) == 0)
    {
        Child() ;
    }
    else
    {
        Parent() ;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值