大家好,又见面了,我是你们的朋友全栈君。
操作系统课设详细解答
一、题目一
实验一 Windows 进程管理
二、实验目的
(1)学会使用 VC 编写基本的 Win32 Consol Application(控制台应用程序)。 (2)通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉操作系统的进程概念,理解 Windows 进程的“一生”。 (3)通过阅读和分析实验程序,学习创建进程、观察进程、终止进程以及父子进程同步的基本程序设计方法。
三、总体设计
1.背景知识
Windows 所创建的每个进程都从调用 CreateProcess() API 函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。每一进程都以调用 ExitProcess() 或 TerminateProcess() API 函数终止。通常应用程序的框架负责调用 ExitProcess() 函数。对于 C 运行库来说,这一调用发生在应用程序的 main() 函数返回之后。
2.模块介绍
创建进程子进程startClone( )模块,主函数模块,互斥信号量的创建与释放。
3.设计步骤
(1)编写基本的 Win32 Consol Application 步骤 1:登录进入 Windows 系统,启动 VC 6.0。 步骤 2:在“FILE”菜单中单击“NEW”子菜单,在“projects”选项卡中选择“Win32 ConsolApplication”,然后在“Project name”处输入工程名,在“Location” 处输入工程目录。创建一个新的控制台应用程序工程。 步骤 3:在“FILE”菜单中单击“NEW”子菜单,在“Files”选项卡中选择“C Source File”,然后在“File” 处输入 C/C 源程序的文件名。 步骤 4:将清单 1-1 所示的程序清单复制到新创建的 C/C 源程序中。编译成可执行文件。 步骤 5:在“开始”菜单中单击“程序”-“附件”-“命令提示符”命令,进入 Windows“命令提示符”窗口,然后进入工程目录中的 debug 子目录,执行编译好的可执行程序,列出运行结果(如果运行不成功,则可能的原因是什么? 答:路径不对或者没有编译文件等。
图1-1 一个简单的 Windows 控制台应用程序输出结果
(2)创建进程 本实验显示了创建子进程的基本框架。该程序只是再一次地启动自身,显示它的系统进程 ID和它在进程列表中的位置。 步骤 1:创建一个“Win32 Consol Application”工程,然后拷贝清单 1-2 中的程序,编译成可执行文件。 步骤 2:在“命令提示符”窗口运行步骤 1 中生成的可执行文件,列出运行结果。按下ctrl alt del,调用 windows 的任务管理器,记录进程相关的行为属性。 步骤 3:在“命令提示符”窗口加入参数重新运行生成的可执行文件,列出运行结果。按下ctrl alt del,调用 windows 的任务管理器,记录进程相关的行为属性。 步骤 4:修改清单 1-2 中的程序,将 nClone 的定义和初始化方法按程序注释中的修改方法进行 修改,编译成可执行文件(执行前请先保存已经完成的工作)。再按步骤 2 中的方式运行,看看结果会有什么不一样。列出行结果。从中你可以得出什么结论?说明 nClone 的作用。 变量的定义和初始化方法(位置)对程序的执行结果有影响吗?为什么? 答:控制程序执行过程,当nClone>5时跳出循环,创建子进程结束;有,在第二次更改中,由于nClone每次都初始化为0,会陷入死循环,不断创建子进程。
图1-2 创建子进程(1)
图1-3 创建子进程(2)
(3)父子进程的简单通信及终止进程 步骤 1:创建一个“Win32 Consol Application”工程,然后拷贝清单 1-3 中的程序,编译成 可执行文件。 步骤 2:在 VC 的工具栏单击“Execute Program”(执行程序) 按钮,或者按 Ctrl F5 键,或者 在“命令提示符”窗口运行步骤 1 中生成的可执行文件,列出运行结果。 步骤 3:按源程序中注释中的提示,修改源程序 1-3,编译执行(执行前请先保存已经完成的 工作),列出运行结果。在程序中加入跟踪语句,或调试运行程序,同时参考 MSDN 中的帮助文件 CreateProcess()的使用方法,理解父子进程如何传递参数。给出程序执行过程的大概描述。 步骤 4:按源程序中注释中的提示,修改源程序 1-3,编译执行,列出运行结果。 步骤 5:参考 MSDN 中的帮助文件 CreateMutex() 、 OpenMutex() 、 ReleaseMutex() 和WaitForSingleObject()的使用方法,理解父子进程如何利用互斥体进行同步的。给出父子进程同步过程的一个大概描述。 答:CreateMutex() 创建互斥体hMutexSuicide信号、 OpenMutex()打开互斥体、 ReleaseMutex()释放互斥体、WaitForSingleObject()检测Hhandle信号状态,通过这些只允许有一个状态被创建或者使用也就是信号量唯一,实现进程同步。
图1-4父子进程的简单通信及终止进程的示例程序
四、详细设计
- 数据结构 数组、函数调用,父子进程参数的传递、父子进程利用互斥信号进行同步、互斥体的创建、获得、检测与释放、API接口等。
- 程序流程图
图1-5 一个简单的 Windows 控制台应用程序流程图
图1-6 创建子进程流程图
图1-7父子进程的简单通信及终止进程的示例程序流程图
3. 关键代码 1-1 一个简单的 Windows 控制台应用程序
代码语言:javascript复制#include <windows.h>
#include <iostream>
#include <stdio.h>
// hello 项目
# include <iostream>
void main()
{
std::cout << “Hello, Win32 Consol Application” << std :: endl ;
}
1-2 创建子进程
代码语言:javascript复制// 创建传递过来的进程的克隆过程并赋于其 ID 值
void StartClone(int nCloneID)
{
// 提取用于当前可执行文件的文件名
TCHAR szFilename[MAX_PATH] ;
GetModuleFileName(NULL, szFilename, MAX_PATH) ;
// 格式化用于子进程的命令行并通知其 EXE 文件名和克隆 ID
TCHAR szCmdLine[MAX_PATH];
sprintf(szCmdLine,""%s" %d",szFilename,nCloneID);
// 用于子进程的 STARTUPINFO 结构
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) ;
}
}
1-3 父子进程的简单通信及终止进程的示例程序
代码语言:javascript复制// procterm 项目
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, // 最初拥有的
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) ;
}
}
五、实验结果与分析
实验1-1结果分析:修改void成int,然后加上return 0,从main()函数开始,运行输出Hello, Win32 Consol Application。 实验1-2结果分析:从main()函数开始,首先判断argc的值(argc初始值默认为1)因为argc不满足大于1,所以不能将argv[1]赋值给nClone;然后nClone < c_nCloneMax ,则调用StartClone ( nClone)函数,创建子进程;创建子进程后,argc的值变为2,然后将自增的nClone赋值argv[1],然后将继续执行main()函数,直到(nClone >c_nCloneMax),跳出,结束创建新进程。 实验1-3结果分析:从main()函数开始,首先判断argc的值(argc初始值默认为1),决定进行父进程还是子进程,因为argc不满足大于1,所以调用parent()函数,在执行parent()函数过程中调用StartClone() ;然后通过sprintf(szCmdLine, “”%s”child” , szFilename)将argv[1]赋值child,后面满足条件后调用child()函数;由于设置了互斥信号,则只允许一个进程进行,所以只有当父进程释放互斥信号hMutexSuicide时,子进程检测获得才结束进程。
六、小结与心得体会
通过这个实验加深了我对操作系统的进程概念的了解,理解 Windows 进程的“一生”所有进程都是以调用CreateProcess()API函数开始的ExitProcess函数结束的。利用 CreateMutex() API 可创建互斥体,创建时还可以指定一个初始的拥有权标志,通过使用这个标志,只有当线程完成了资源的所有的初始化工作时,才允许创建线程释放互斥体,放弃共享资源时需要在该对象上调用 ReleaseMute() API函数等。
一、题目二
实验二 Linux 进程管理
二、实验目的
通过进程的创建、撤销和运行加深对进程概念和进程并发执行的理解,明确进程和程序之间的区别。
三、总体设计
1.背景知识
在 Linux 中创建子进程要使用 fork()函数,执行新的命令要使用 exec()系列函数,等待子进 程结束使用 wait()函数,结束终止进程使用 exit()函数。fork()原型如下:pid_t fork(void);fork 建立一个子进程,父进程继续运行,子进程在同样的位置执行同样的程序。对于父进程,fork()返回子进程的 pid, 对于子进程,fork()返回 0。出错时返回-1。
2.模块介绍
2-1:一个父进程,两个子进程 2-2:一个父进程,一个子进程 2-3:一个父进程,多个子进程
3.设计步骤
(1)进程的创建 任务要求:编写一段程序,使用系统调用 fork()创建两个子进程。当此程序运行时,在系统 中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”; 两子进程分别显示字符“b”和字符“c”。 步骤 1:使用 vi 或 gedit 新建一个 fork_demo.c 程序,然后拷贝清单 2-1 中的程序,使用 cc 或者gcc 编译成可执行文件 fork_demo。例如,可以使用 gcc –o fork_demo fork_demo.c 完成编译。 步骤 2:在命令行输入./fork_demo 运行该程序。
图2-1 进程的创建输出结果
(2)子进程执行新任务 任务要求:编写一段程序,使用系统调用 fork()创建一个子进程。子进程通过系统调用 exec 更换自己原有的执行代码,转去执行 Linux 命令/bin/ls (显示当前目录的列表),然后调用 exit()函 数结束。父进程则调用 waitpid()等待子进程结束,并在子进程结束后显示子进程的标识符,然后正 常结束。程序执行过程如图 2-1 所示。 步骤 1:使用 vi 或 gedit 新建一个 exec_demo.c 程序,然后拷贝清单 2-2 中的程序(该程序的执 行如图 2-1 所示),使用 cc 或者 gcc 编译成可执行文件 exec_demo。例如,可以使用 gcc –o exec_demo exec_demo.c 完成编译。 步骤 2:在命令行输入./exec_demo 运行该程序。 步骤 3:观察该程序在屏幕上的显示结果,并分析。
图2-2 子进程执行新任务输出结果
(3)实现一个简单的 shell(命令行解释器) (此任务有一些难度,可选做)。 任务要求:要设计的 shell 类似于 sh,bash,csh 等,必须支持以下内部命令: cd <目录>更改当前的工作目录到另一个<目录>。如果<目录>未指定,输出当前工作目录。如 果<目录>不存在,应当有适当的错误信息提示。这个命令应该也能改变 PWD 的环境变量。 environ 列出所有环境变量字符串的设置(类似于 Unix 系统下的 env 命令)。 echo <内容 > 显示 echo 后的内容且换行 help 简短概要的输出你的 shell 的使用方法和基本功能。 jobs 输出 shell 当前的一系列子进程,必须提供子进程的命名和 PID 号。 quit,exit,bye 退出 shell。
图2-3 实现一个简单的 shell输出结果
四、详细设计
- 数据结构 一个进程创建多个子进程时,则子进程之间具有兄弟关系,数据结构为链表结构,也运用了一些C 库函数。
- 程序流程图
图2-4 进程的创建流程图
图2-5 子进程执行新任务流程图
图2-6 实现一个简单的 shell(命令行解释器)流程图
3. 关键代码 2-1 创建进程
代码语言:javascript复制#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main ()
{
int x;
while((x=fork())==-1);
if (x==0){
x=fork();
if(x>0)
printf("b");
else
printf("c");
}
else
printf("a");
}
2-2 子进程执行新任务
代码语言:javascript复制#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
/* fork a child process */
pid = fork();
if (pid < 0)
{ /* error occurred */
fprintf(stderr, "Fork Failed");
return 1;
}
else if (pid == 0)
{ /* 子进程 */
execlp("/bin/ls","ls",NULL);
}
else { /* 父进程 */
/* 父进程将一直等待,直到子进程运行完毕*/
wait(NULL);
printf("Child Complete");
}
return 0;
} }
return 0;
}
2-3 实现一个简单的 shell(命令行解释器) (选做)
代码语言:javascript复制#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
char cmd[666];
char cata[100];
while(1)
{
int len,i,flag,cnt;
printf("Enter commands:");
// print String
scanf("%s",cmd);
// Calculation String
len = strlen(cmd);
// for cd
if(cmd[0]=='c')
{
flag=0;
cnt=0;
// Start after command
for(i=3; i<len-1; i )
{
// String is not null
if(cmd[i]!=' ') flag=1;
if(flag)
{
cata[cnt ] = cmd[i];
}
}
// String is null
if(cnt==0)
{
printf("path error!n");
cata[0]='.';
cata[1]='