操作系统实验三
操作系统实验,实验3, 进程管理 (1)

在图形界面上利用终端通过命令“su - ”切换到超级用户,编辑文件 “job”; 按组合键[Ctrl+Alt+F2]切换到第二个虚拟终端(超级用户); 输入命令“at –f job now+1 minute”,设置1分钟后执行at调度内容; 稍等后观察at调度的执行效果,再切换到第一个虚拟终端观察at调度 的执行效果;
切换到第一个虚拟终端观察at调度的执行效果(5分钟后系统将执行重 启调度任务)。
[操作要求2] 设置一个调度,要求1分钟后执行文件job中的作业。 文件job的内容为: find /home/jkx/ -name “*.c” > /home/jkx/fdresult wall user jkx, all code files have been searched out! Please check out. [操作步骤]
续表
守 护 进 程 innd Usenet新闻服务器 功 能 说 明
linuxconf
lpd named netfs network
允许使用本地WEB服务器作为用户接口来配置机器
打印服务器 DNS服务器 安装NFS、Samba和NetWare网络文件系统 激活已配置网络接口的脚本程序
nfsd
portmap postgresql routed sendmail
事件(例如xinetd和lpd)
启动守护进程有如下几种方法
在引导系统时启动 人工手动从shell提示符启动
系统启动script的执行期间 被启动(/etc/rc.d) 任何具有相应执行 权限的用户
使用crond守护进程启动
执行at命令启动
守护进程一般由系统在开机时通过脚本或root
操作系统实验3-进程控制

WORD wMajorReq=(WORD)(dwVerReq>16);
WORD wMinorReq=(WORD)(dwVerReq&0xffff);
::cout<<"Process ID:"<<dwIdThis<<",requires OS:"<<wMajorReq<<wMinorReq<<::endl;
{
//改变优先级
::SetPriorityClass(
::GetCurrentProcess(), //利用这一进程
HIGH_PRIORITY_CLASS); //改变为high
//报告给用户
::cout<<"Task Manager should indicate this "
"process is high priority."<<::endl;
//设置版本信息的数据结构,以便保存操作系统的版本信息
OSVERSIONINFOEX osvix;
::ZeroMemory(&osvix,sizeof(osvix));
osvix.dwOSVersionInfoSize=sizeof(osvix);
//提取版本信息和报告
::GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&osvix));
Parent();
}
return 0;
}
分析:程序4-3.cpp说明了一个进程从“生”到“死”的整个一生,第一次执行时,它创建一个子进程,其行为如同“父亲”。在创建子进程之前,先创建一个互斥的内核对象,其行为对于子进程来说,如同一个“自杀弹”。当创建子进程时,就打开了互斥体并在其他线程中进行别的处理工作,同时等待着父进程使用ReleaseMutex()API发出“死亡”信号。然后用Sleep()API调用来模拟父进程处理其他工作,等完成时,指令子进程终止。
操作系统 实验三 进程同步

集美大学计算机工程学院实验报告课程名称:操作系统指导教师:王丰实验成绩:实验编号:实验三实验名称:进程同步班级:计算12姓名:学号:上机实践日期:2015.5上机实践时间:2学时一、实验目的1、掌握用Linux信号灯集机制实现两个进程间的同步问题。
2、共享函数库的创建二、实验环境Ubuntu-VMware、Linux三、实验内容⏹需要的信号灯: System V信号灯实现☐用于控制司机是否可以启动车辆的的信号灯 S1=0☐用于控制售票员是否可以开门的信号灯 S2=0System V信号灯实现说明□ System V的信号灯机制属于信号灯集的形式, 一次可以申请多个信号灯.□同样利用ftok()生成一个key: semkey=ftok(path,45);□利用key申请一个包含有两个信号灯的信号灯集, 获得该集的idsemid=semget(semkey,2,IPC_CREAT | 0666);□定义一个联合的数据类型union semun{int val;struct semid_ds *buf;ushort *array;};□利用semctl()函数对信号灯初始化,参数有:信号灯集的id: semid要初始化的信号灯的编号:sn要设定的初始值:valvoid seminit(int semid, int val,int sn){union semun arg;arg.val=val;semctl(semid,sn,SETVAL,arg);}利用初始化函数,初始化信号灯:seminit(semid,0,0);//用来司机启动汽车的同步seminit(semid,0,1);//用来售票员开门的同步控制□利用semop()函数, 对信号灯实现V操作:sembuf是一个在头部文件中的预定义结构、semid—信号灯集id, sn—要操作的信号灯编号void semdown(int semid,int sn){/* define P operating*/struct sembuf op;op.sem_num=sn;op.sem_op=-1;//P操作为-1op.sem_flg=0;semop(semid,&op,1);}2、Linux的静态和共享函数库·Linux生成目标代码: gcc -c 源程序文件名(将生成一个与源程序同名的.o目标代码文件。
哈工大《操作系统》实验3

向kernel/printk.c中添加日志打印功能,将以下代码添加到原文件中:
在kernel/fork.c、kernel/sched.c和kernel/exit.c中,找到正确的状态转换点,并添加合适的状态信息,把它输出到log文件之中。
fork.c的修改如下:
exit.c的修改如下:
sched.c的修改如下:
在虚拟机上运行ls -l /var”或“ll /var”查看process.log是否建立,及它的属性和长度;
修改时间片
include/linux/sched.h宏INIT_TASK中定义的:
0,15,15, 分别对应state、counter和priority,将priority值修改,即可实现对时间片大小的调整。
0,15,15, 分别对应state、counter和priority,
priority值修改,即可实现对时间片大小的调整。
在修改时间片将priority由15改为150后,Process 9~20 中Turnaround, Waiting, CPU Burst, I/O Burst变化不大,原因可能是程序中I/O操作占用的时间对于总时间影响的权重过大,导致处理时间体现的并不明显。
或者变化不大的原因是,子进程连续占用cpu的时间要比时间片大很多。
电大操作系统实验报告3_ 进程管理实验

电大操作系统实验报告3_ 进程管理实验电大操作系统实验报告 3 进程管理实验一、实验目的进程管理是操作系统的核心功能之一,本次实验的目的是通过实际操作和观察,深入理解进程的概念、状态转换、进程调度以及进程间的通信机制,掌握操作系统中进程管理的基本原理和方法,提高对操作系统的整体认识和实践能力。
二、实验环境本次实验使用的操作系统为 Windows 10,编程语言为 C 语言,开发工具为 Visual Studio 2019。
三、实验内容及步骤(一)进程的创建与终止1、编写一个 C 程序,使用系统调用创建一个子进程。
2、在父进程和子进程中分别输出各自的进程 ID 和父进程 ID。
3、子进程执行一段简单的计算任务,父进程等待子进程结束后输出结束信息。
以下是实现上述功能的 C 程序代码:```cinclude <stdioh>include <stdlibh>include <unistdh>int main(){pid_t pid;pid = fork();if (pid < 0) {printf("创建子进程失败\n");return 1;} else if (pid == 0) {printf("子进程:我的进程 ID 是%d,父进程 ID 是%d\n",getpid(), getppid());int result = 2 + 3;printf("子进程计算结果:2 + 3 =%d\n", result);exit(0);} else {printf("父进程:我的进程 ID 是%d,子进程 ID 是%d\n",getpid(), pid);wait(NULL);printf("子进程已结束\n");}return 0;}```编译并运行上述程序,可以观察到父进程和子进程的输出信息,验证了进程的创建和终止过程。
(二)进程的状态转换1、编写一个 C 程序,创建一个子进程,子进程进入睡眠状态一段时间,然后被唤醒并输出状态转换信息。
操作系统实验3进程的创建控制实验

操作系统实验3进程的创建控制实验实验三的目标是通过实现一个进程控制程序,来加深我们对进程创建和控制机制的理解,并通过实践来熟悉和掌握相关的编程技巧。
在进行实验之前,我们需要先了解进程的一些基本概念和相关知识。
首先,进程的创建是通过操作系统中的系统调用来完成的。
在Linux系统中,常用的创建进程的系统调用有fork(和exec(。
fork(系统调用可以创建一个新的进程,该进程与调用fork(的进程几乎完全相同;而exec(系统调用则在新创建的进程中执行一个新的程序。
另外,进程的控制机制主要是通过进程的状态来实现的。
进程可以处于就绪状态、运行状态和阻塞状态。
就绪状态的进程可以被调度器选择后立即运行,而阻塞状态的进程则需要等待一些条件满足后才能被唤醒并变为就绪状态。
实验三的具体内容包括:1. 编写一个程序,通过调用fork(创建多个子进程。
子进程和父进程可以并行执行,共享程序的代码和数据段。
2. 子进程通过调用exec(系统调用执行不同的程序。
可以通过调用不同的exec(函数或者传入不同的参数来执行不同的程序。
3. 子进程执行的程序可能会产生不同的结果,比如输出不同的字符串或者产生不同的返回值。
我们可以通过wait(系统调用等待子进程退出,并获取子进程的返回值。
4. 父进程可以通过调用waitpid(系统调用来选择等待一些特定的子进程,以及获取特定子进程的返回值。
通过实验三的实践,我将更加深入地了解进程的创建和控制机制。
实验三的实验结果将让我熟悉和掌握相关的编程技巧,为我今后更加熟练地编写和控制进程打下坚实的基础。
总之,实验三是一个非常有意义的实验,将帮助我更加深入地理解进程的创建和控制机制,并通过实践获得相关的编程技巧。
这将对我今后的学习和实践有很大的帮助。
16207318邓嘉操作系统实验三
操作系统实验第三次实验进程同步实验指导老师:***学号:********姓名:***操作系统第三次实验进程同步实验指导老师:谭朋柳学生:16207318邓嘉4.1 实验目的加深对并发协作进程同步与互斥概念的理解,观察和体验并发进程同步与互斥操作的效果,分析与研究经典进程同步与互斥问题的实际解决方案。
了解Linux 系统中IPC 进程同步工具的用法,练习并发协作进程的同步与互斥操作的编程与调试技术。
4.2 实验说明在linux 系统中可以利用进程间通信(interprocess communication )IPC 中的3 个对象:共享内存、信号灯数组、消息队列,来解决协作并发进程间的同步与互斥的问题。
1)共享内存是OS 内核为并发进程间交换数据而提供的一块内存区(段)。
如果段的权限设置恰当,每个要访问该段内存的进程都可以把它映射到自己私有的地址空间中。
如果一进程更新了段中数据,那么其他进程立即会看到这一更新。
进程创建的段也可由另一进程读写。
linux 中可用命令ipcs -m 观察共享内存情况。
$ ipcs -m------ Shared Memory Segments --------key shmid owner perms bytes nattch status 0x00000000 327682 student 600 393216 2 dest0x00000000 360451 student 600 196608 2 dest 0x00000000 393220 student 600 196608 2 destkey 共享内存关键值shmid 共享内存标识owner 共享内存所由者(本例为student)perm 共享内存使用权限(本例为student 可读可写)byte 共享内存字节数nattch 共享内存使用计数status 共享内存状态上例说明系统当前已由student 建立了一些共享内存,每个都有两个进程在共享。
操作系统实验三(页面置换算法)实验报告
实验三实验报告实验源码:#include "stdio.h"#include <iostream.h>#include <stdlib.h>#define DataMax 100 // 常量DataMax#define BlockNum 10 // 常量BlockNumint DataShow[BlockNum][DataMax]; // 用于存储要显示的数组bool DataShowEnable[BlockNum][DataMax]; // 用于存储数组中的数据是否需要显示int Data[DataMax]; // 保存数据int Block[BlockNum]; // 物理块int count[BlockNum]; // 计数器int N; // 页面个数int M; // 最小物理块数int ChangeTimes; // 缺页次数void DataInput(); // 输入数据的函数void DataOutput(); // 输出数据的函数void FIFO(); // FIFO 函数void Optimal(); // Optimal函数void LRU(); // LRU函数int main(int argc, char* argv[]){DataInput();int menu;while(true){printf("\n* 菜单选择*\n");printf("*******************************************************\n");printf("* 1-Optimal *\n");printf("* 2-FIFO *\n");printf("* 3-LRU *\n");printf("* 4-返回上一级*\n");printf("* 0-EXIT *\n");printf("*******************************************************\n");scanf("%d",&menu);switch(menu){case 1:Optimal();break;case 2:FIFO();break;case 3:LRU();break;case 0:exit(0);break;case 4:system("cls");DataInput();break;}if(menu != 1 && menu != 2 && menu != 3 && menu != 0 && menu !=4) { system("cls");printf("\n请输入0 - 4之间的整数!\n");continue;}}return 0;}void DataInput(){int i,choice;printf("请输入最小物理块数:");scanf("%d",&M);// 输入最小物理块数大于数据个数while(M > BlockNum){printf("物理块数超过预定值,请重新输入:");scanf("%d",&M);}printf("请输入页面的个数:");scanf("%d",&N);// 输入页面的个数大于数据个数while(N > DataMax){printf("页面个数超过预定值,请重新输入:");scanf("%d",&N);}printf("请选择产生页面访问序列的方式(1.随机2.输入):");scanf("%d",&choice);switch(choice){case 1:// 产生随机访问序列for(i = 0;i < N;i++){Data[i] = (int)(((float) rand() / 32767) * 10); // 随机数大小在0 - 9之间}system("cls");// 显示随机产生的访问序列printf("\n随机产生的访问序列为:");for(i = 0;i < N;i++){printf("%d ",Data[i]);}printf("\n");break;case 2:// 输入访问序列printf("请输入页面访问序列:\n");for(i = 0;i < N;i++)scanf("%d",&Data[i]);system("cls");// 显示输入的访问序列printf("\n输入的访问序列为:");for(i = 0;i < N;i++){printf("%d ",Data[i]);}printf("\n");break;default:while(choice != 1 && choice != 2){printf("请输入1或2选择相应方式:");scanf("%d",&choice);}break;}}void DataOutput(){int i,j;// 对所有数据操作for(i = 0;i < N;i++){printf("%d ",Data[i]);}printf("\n");for(j = 0;j < M;j++){// 对所有数据操作for(i = 0;i < N;i++){if( DataShowEnable[j][i] )printf("%d ",DataShow[j][i]);elseprintf(" ");}printf("\n");}printf("缺页次数: %d\n",ChangeTimes);printf("缺页率: %d %%\n",ChangeTimes * 100 / N); }// 最佳置换算法void Optimal(){int i,j,k;bool find;int point;int temp; // 临时变量,比较离的最远的时候用int m = 1,n;ChangeTimes = 0;for(j = 0;j < M;j++){for(i=0;i < N;i++){DataShowEnable[j][i] = false; // 初始化为false,表示没有要显示的数据}}for(i = 0;i < M;i++){count[i] = 0 ; // 初始化计数器}// 确定当前页面是否在物理块中,在继续,不在置换/////////////////////////////////////////////////////////////////////////////////// Block[0] = Data[0];for(i = 1;m < M;i++){int flag = 1;for(n = 0; n < m;n++){if(Data[i] == Block[n]) flag = 0;}if(flag == 0) continue;Block[m] = Data[i];m++;}//////////////////////////////////////////////////////////////////////////////////// 对所有数据进行操作for(i=0;i < N;i++){// 表示块中有没有该数据find = false;for(j = 0;j < M;j++){if( Block[j] == Data[i] ){find = true;}}if( find ) continue; // 块中有该数据,判断下一个数据// 块中没有该数据,最优算法ChangeTimes++; // 缺页次数++for(j = 0;j < M;j++){// 找到下一个值的位置find = false;for( k = i;k < N;k++){if( Block[j] == Data[k] ){find = true;count[j] = k;break;}}if( !find ) count[j] = N;}// 因为i是从0开始记,而BlockNum指的是个数,从1开始,所以i+1if( (i + 1) > M ){//获得要替换的块指针temp = 0;for(j = 0;j < M;j++){if( temp < count[j] ){temp = count[j];point = j; // 获得离的最远的指针}}}else point = i;// 替换Block[point] = Data[i];// 保存要显示的数据for(j = 0;j < M;j++){DataShow[j][i] = Block[j];DataShowEnable[i < M ? (j <= i ? j : i) : j][i] = true; // 设置显示数据}}// 输出信息printf("\nOptimal => \n");DataOutput();}// 先进先出置换算法void FIFO(){bool find;int point;int temp; // 临时变量int m = 1,n;ChangeTimes = 0;for(j = 0;j < M;j++){for(i = 0;i < N;i++){DataShowEnable[j][i] = false; // 初始化为false,表示没有要显示的数据}}for(i = 0;i < M;i++){count[i] = 0; // 大于等于BlockNum,表示块中没有数据,或需被替换掉// 所以经这样初始化(3 2 1),每次替换>=3的块,替换后计数值置1,// 同时其它的块计数值加1 ,成了(1 3 2 ),见下面先进先出程序段}// 确定当前页面是否在物理块中,在继续,不在置换/////////////////////////////////////////////////////////////////////////////////// Block[0] = Data[0];for(i = 1;m < M;i++){int flag = 1;for(n = 0; n < m;n++){if(Data[i] == Block[n]) flag = 0;}if(flag == 0) continue;Block[m] = Data[i];m++;}//////////////////////////////////////////////////////////////////////////////////// 对有所数据操作for(i = 0;i < N;i++){// 增加countfor(j = 0;j < M;j++){count[j]++;find = false; // 表示块中有没有该数据for(j = 0;j < M;j++){if( Block[j] == Data[i] ){find = true;}}// 块中有该数据,判断下一个数据if( find ) continue;// 块中没有该数据ChangeTimes++; // 缺页次数++// 因为i是从0开始记,而M指的是个数,从1开始,所以i+1if( (i + 1) > M ){//获得要替换的块指针temp = 0;for(j = 0;j < M;j++){if( temp < count[j] ){temp = count[j];point = j; // 获得离的最远的指针}}}else point = i;// 替换Block[point] = Data[i];count[point] = 0; // 更新计数值// 保存要显示的数据for(j = 0;j < M;j++){DataShow[j][i] = Block[j];DataShowEnable[i < M ? (j <= i ? j : i) : j][i] = true; // 设置显示数据}}// 输出信息printf("\nFIFO => \n");DataOutput();}// 最近最久未使用置换算法void LRU(){int i,j;bool find;int point;int temp; // 临时变量int m = 1,n;ChangeTimes = 0;for(j = 0;j < M;j++){for(i = 0;i < N;i++){DataShowEnable[j][i] = false; // 初始化为false,表示没有要显示的数据}}for(i = 0;i < M;i++){count[i] = 0 ; // 初始化计数器}// 确定当前页面是否在物理块中,在继续,不在置换///////////////////////////////////////////////////////////////////////////////////Block[0] = Data[0];for(i = 1;m < M;i++){int flag = 1;for(n = 0; n < m;n++){if(Data[i] == Block[n]) flag = 0;}if(flag == 0) continue;Block[m] = Data[i];m++;}//////////////////////////////////////////////////////////////////////////////////// 对有所数据操作for(i = 0;i < N;i++){// 增加countfor(j = 0;j < M;j++){count[j]++;}find = false; // 表示块中有没有该数据for(j = 0;j < M;j++){if( Block[j] == Data[i] ){count[j] = 0;find = true;}}// 块中有该数据,判断下一个数据if( find ) continue;// 块中没有该数据ChangeTimes++;// 因为i是从0开始记,而BlockNum指的是个数,从1开始,所以i+1 if( (i + 1) > M ){//获得要替换的块指针temp = 0;for(j = 0;j < M;j++){if( temp < count[j] ){temp = count[j];point = j; // 获得离的最远的指针}}}else point = i;// 替换Block[point] = Data[i];count[point] = 0;// 保存要显示的数据for(j=0;j<M;j++){DataShow[j][i] = Block[j];DataShowEnable[i < M ?(j <= i ? j : i) : j][i] = true; // 设置显示数据}}// 输出信息printf("\nLRU => \n");DataOutput();}实验结果截图:程序运行:输入相应数据:选择相应算法:最佳置换算法:先进先出算法:最近最久未使用算法:。
操作系统实验三实验报告
(一)进程创建
编写程序实现创建多个进程,并观察进程的执行情况。通过调用Windows API函数`CreateProcess`来创建新的进程。在创建进程时,设置不同的参数,如进程的优先级、命令行参数等,观察这些参数对进程执行的影响。
(二)进程控制
实现对进程的暂停、恢复和终止操作。使用`SuspendThread`和`ResumeThread`函数来暂停和恢复进程中的线程,使用`TerminateProcess`函数来终止进程。通过控制进程的执行状态,观察系统的资源使用情况和进程的响应。
(一)进程创建实验结果与分析
创建多个进程后,通过任务管理器观察到新创建的进程在系统中运行。不同的进程优先级设置对进程的CPU占用和响应时间产生了明显的影响。高优先级的进程能够更快地获得CPU资源,执行速度相对较快;而低优先级的进程则在CPU资源竞争中处于劣势,可能会出现短暂的卡顿或计一个多进程同步的程序,使用信号量、互斥量等同步机制来协调多个进程的执行。例如,实现一个生产者消费者问题,多个生产者进程和消费者进程通过共享缓冲区进行数据交换,使用同步机制来保证数据的一致性和正确性。
四、实验步骤
(一)进程创建实验步骤
1、打开Visual Studio 2019,创建一个新的C++控制台应用程序项目。
六、实验中遇到的问题及解决方法
(一)进程创建失败
在创建进程时,可能会由于参数设置不正确或系统资源不足等原因导致创建失败。通过仔细检查参数的设置,确保命令行参数、环境变量等的正确性,并释放不必要的系统资源,解决了创建失败的问题。
(二)线程控制异常
在暂停和恢复线程时,可能会出现线程状态不一致或死锁等异常情况。通过合理的线程同步和错误处理机制,避免了这些异常的发生。在代码中添加了对线程状态的判断和异常处理的代码,保证了线程控制的稳定性和可靠性。
实验三 Linux操作系统安全实验 (一)
实验三Linux操作系统安全一、实验目的及要求(一)实验目的通过实验熟悉Linux环境下的用户管理、进程管理以及文件管理的相关操作命令。
掌握linux操作系统中相关的系统安全配置方法,建立linux操作系统的基本安全框架。
(二)实验要求根据实验中介绍的Linux操作系统的各项安全性实验要求,详细观察记录设置前后系统的变化,给出分析报告。
使用RPM对系统的软件进行管理,验证系统内软件的完整性,并分析结果。
试比较Linux下网了服务安全设置与Windows下安全设置异同。
二、实验环境安装Red hat9.0操作系统的计算机一台三、实验内容1、账户和口令安全2、文件系统管理安全3、查看和更改PAM模块设置4、RPM软件管理5、系统安全配置四、实验步骤任务一账户和口令安全1、查看和添加账户(1)在X_Windows窗口中单击鼠标右键,选择“信件中断”,输入下面的命令行:[root@localhost root]#useradd mylist利用useradd命令新建名为mylist的新账户。
(2)输入命令行:[root@localhost root]#cat/etc/shadow利用cat查看系统中的账户列表。
用su命令切换到新建的账户,重复步骤(2),检查shadow文件的权限设置是否安全。
设置安全时,普通用户mylist应该没有查看该系统文件的权限。
在终端中出现如下的提示:Cat:/etc/shadow:权限不够2、添加和更改密码(1)在终端输入[root@localhost root]#passwd mylist为新建账户添加密码。
注意:系统管理员无需输入原来密码即可以利用passwd命令添加或改变任意用户的密码,但普通用户只能改变自己的密码。
(2)输入后依次出现如下提示:Changjing passwd for user mylist.New passwd:Retype new passwd:Passwd:all authentication tokens updated susscessfully.输入密码,Linux系统不会将输入显示出来。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
操作系统实验报告哈尔滨工程大学计算机科学与技术学院第三讲进程的创建一、实验概述1. 实验名称进程的创建2. 实验目的✍✍练习使用EOS API函数CreateProcess创建一个进程,掌握创建进程的方法,理解进程和程序的区别。
✍✍调试跟踪CreateProcess函数的执行过程,了解进程的创建过程,理解进程是资源分配的单位。
3. 实验类型设计4. 实验内容4.1 准备实验按照下面的步骤准备本次实验:1. 启动OS Lab。
2. 新建一个EOS Kernel项目。
3. 分别使用Debug配置和Release配置生成此项目,从而在该项目文件夹中生成完全版本的EOS SDK文件夹。
4. 新建一个EOS应用程序项目。
5. 使用在第3步生成的SDK文件夹覆盖EOS应用程序项目文件夹中的SDK文件夹。
4.2 练习使用控制台命令创建EOS应用程序的进程练习使用控制台命令创建EOS应用程序进程的具体步骤如下:1. 在EOS应用程序项目的“项目管理器”窗口中双击Floppy.img文件,使用FloppyImageEditor工具打开此软盘镜像文件。
2. 将本实验文件夹中的Hello.exe文件拖动到FloppyImageEditor工具窗口的文件列表中释放,Hello.exe文件即被添加到软盘镜像文件中。
Hello.exe一个EOS应用程序,其源代码可以参见本实验文件夹中的Hello.c源文件。
3. 在FloppyImageEditor中选择“文件”菜单中的“保存”后关闭FloppyImageEditor。
4. 按F7生成EOS应用项目。
5. 按F5启动调试。
OS Lab会弹出一个调试异常对话框,并中断应用程序的执行。
6. 在调试异常对话框中选择“否”,忽略异常继续执行应用程序。
7. 激活虚拟机窗口,待该应用程序执行完毕后,在EOS的控制台中输入命令“A:\Hello.exe”后回车。
8. Hello.exe应用程序开始执行,观察其输出。
9. 待Hello.exe执行完毕后可以重复第7步,或者结束此次调试。
4.3 练习通过编程的方式让应用程序创建另一个应用程序的进程使用OS Lab打开本实验文件夹中的NewProc.c文件(将此文件拖动到OS Lab窗口中释放即可),仔细阅读此文件中的源代码和注释。
按照下面的步骤查看应用程序创建另一个应用程序的进程的执行结果:1. 使用NewProc.c文件中的源代码替换之前创建的EOS应用程序项目中的EOSApp.c文件内的源代码。
2. 按F7生成修改后的EOS应用程序项目。
3. 按F5启动调试。
OS Lab会首先弹出一个调试异常对话框。
4. 在调试异常对话框中选择“否”,继续执行。
5. 激活虚拟机窗口查看应用程序输出的内容。
可以看到父进程(EOSApp.exe)首先开始执行并输出内容,父进程创建了子进程(Hello.exe)后,子进程开始执行并输出内容,待子进程结束后父进程再继续执行。
6. 结束此次调试。
4.4 调试CreateProcess函数按照下面的步骤调试CreateProcess函数创建进程的过程:1. 按F5启动调试EOS应用程序,OS Lab会首先弹出一个调试异常对话框。
2. 选择“是”调试异常,调试会中断。
3. 在main函数中调用CreateProcess函数的代码行(第57行)添加一个断点。
4. 按F5继续调试,在断点处中断。
5. 按F11调试进入CreateProcess函数。
此时已经开始进入EOS内核进行调试。
当EOS应用程序eosapp.exe存储在软盘上的时候,它是静态的,只包含应用程序的指令和数据。
而创建进程后,进程不但包含应用程序的指令和数据,也会包含操作系统内核(kernel.dll)的指令和数据(参见图5-1)。
同时,图11-4也说明了一个进程可以包含多个程序,该进程包含了eosapp.exe和kernel.dll 两个程序。
可以按照下面的步骤来分别验证应用程序和操作系统内核在进程的4G虚拟地址空间中所处的位置:1. 由于此时在内核的CreateProcess函数内中断执行,所以在“调试”菜单的“窗口”中选择“反汇编”,会在“反汇编”窗口中显示CreateProcess函数的指令对应的反汇编代码。
“反汇编”窗口的左侧显示的是指令所在的虚拟地址。
可以看到所有指令的虚拟地址都大于,说明内核(kernel.dll)处于高2G的虚拟地址空间中。
2. 在“调用堆栈”窗口中双击main函数项,设置main函数的调用堆栈帧为活动的。
在“反汇编”窗口中查看main函数的指令所在的虚拟地址都是小于,说明应用程序(eosapp.exe)处于低2G的虚拟地址空间中。
3. 在“调用堆栈”窗口中双击CreateProcess函数项,重新设置CreateProcess函数的调用堆栈帧为活动的。
关闭“反汇编”窗口。
接下来观察eosapi.c文件中CreateProcess函数的源代码,可以看到此函数只是调用了EOS内核函数PsCreateProcess并将创建进程所用到的参数传递给了此函数。
所以,按F11可以调试进入create.c文件中的PsCreateProcess函数,在此函数中才开始执行创建进程的各项操作。
4.5 调试PsCreateProcess函数创建进程最主要的操作就是创建进程控制块(PCB),并初始化其中的各种信息(也就是为进程分配各种资源)。
所以在PsCreateProcess函数中首先调用了PspCreateProcessEnvironment函数来创建进程控制块。
调试PspCreateProcessEnvironment函数的步骤如下:1. 在PsCreateProcess函数中找到调用PspCreateProcessEnvironment函数的代码行(create.c文件的第163行),并在此行添加一个断点。
2. 按F5继续调试,到此断点处中断。
3. 按F11调试进入PspCreateProcessEnvironment函数。
由于PspCreateProcessEnvironment函数的主要功能是创建进程控制块并初始化其中的部分信息,所以在此函数的开始,定义了一个进程控制块的指针变量NewProcess。
在此函数中查找到创建进程控制块的代码行(create.c文件的第418行)Status = ObCreateObject( PspProcessType,NULL,sizeof(PROCESS) + ImageNameSize + CmdLineSize,0,(PVOID*)&NewProcess ); 这里的ObCreateObject函数会在由EOS内核管理的内存中创建了一个新的进程控制块(也就是分配了一块内存),并由NewProcess返回进程控制块的指针(也就是所分配内存的起始地址)。
按照下面的步骤调试进程控制块的创建过程:1. 在调用ObCreateObject函数的代码行(create.c文件的第418行)添加一个断点。
2. 按F5继续调试,到此断点处中断。
3. 按F10执行此函数后中断。
4. 此时为了查看进程控制块中的信息,将表达式*NewProcess添加到“监视”窗口中。
5. 将鼠标移动到“监视”窗口中此表达式的“值”属性上,会弹出一个临时窗口,在临时窗口中会按照进程控制块的结构显示各个成员变量的值(可以参考PROCESS结构体的定义)。
由于只是新建了进程控制块,还没有初始化其中成员变量,所以值都为0。
接下来调试初始化进程控制块中各个成员变量的过程:1. 首先创建进程的地址空间,即4G虚拟地址空间。
在代码行(create.c文件的第437行) NewProcess->Pas = MmCreateProcessAddressSpace(); 添加一个断点。
2. 按F5继续调试,到此断点处中断。
3. 按F10执行此行代码后中断。
4. 在“监视”窗口中查看进程控制块的成员变量Pas的值已经不再是0。
说明已经初始化了进程的4G虚拟地址空间。
5. 使用F10一步步调试PspCreateProcessEnvironment函数中后面的代码,在调试的过程中根据执行的源代码,查看“监视”窗口中*NewProcess表达式的值,观察进程控制块中哪些成员变量是被哪些代码初始化的,哪些成员变量还没有被初始化。
6. 当从PspCreateProcessEnvironment函数返回到PsCreateProcess函数后,停止按F10。
此时“监视”窗口中已经不能再显示表达式*NewProcess的值了,在PsCreateProcess函数中是使用ProcessObject指针指向进程控制块的,所以将表达式*ProcessObject添加到“监视”窗口中就可以继续观察新建进程控制块中的信息。
7. 接下来继续使用F10一步步调试PsCreateProcess函数中的代码,同样要注意观察执行后的代码修改了进程控制块中的哪些成员变量。
当调试到PsCreateProcess函数的最后一行代码时,查看进程控制块中的信息,此时所有的成员变量都已经被初始化了(注意观察成员ImageName的值)。
8. 按F5继续执行,EOS内核会为刚刚初始化完毕的进程控制块新建一个进程。
激活虚拟机窗口查看新建进程执行的结果。
9. 在OS Lab中选择“调试”菜单中的“停止调试”结束此次调试。
10. 选择“调试”菜单中的“删除所有断点”。
4.6 练习通过编程的方式创建应用程序的多个进程使用OS Lab打开本实验文件夹中的参考源代码文件NewTwoProc.c,仔细阅读此文件中的源代码。
使用NewTwoProc.c文件中的源代码替换EOS应用程序项目中EOSApp.c文件内的源代码,生成后启动调试,查看多个进程并发执行的结果。
多个进程并发时,EOS操作系统中运行的用户进程可以参见图11-5。
验证一个程序(hello.exe)可以同时创建多个进程。
二、实验环境操作系统集成实验环境OS Lab? EOS 操作系统三、实验过程1. 设计思路和流程图2. 需要解决的问题及解答在PsCreateProcess函数中调用了PspCreateProcessEnvironment函数后又先后调用了PspLoadProcessImage和PspCreateThread函数,学习这些函数的主要功能。
能够交换这些函数被调用的顺序吗?思考其中的原因。
答:PspCreateProcessEnvironment了句柄表。
PspLoadProcessImage是将进程的可执行映像加载到了进程的地址空间中。
PspCreateThread创必须已经为进程创建了地址空间线程之前必须已经加载了可执行映像这样主线程才能够知道自己要从哪里开始执行,执行哪些指令。